← 返回首页
Python3基础教程(八十)
发表时间:2022-05-14 10:47:13
asyncio

异步IO:就是发起一个IO操作(如:网络请求,文件读写等),这些操作一般是比较耗时的,不用等待它结束,可以继续做其他事情,结束时会发来通知。

1.asyncio

普通函数的定义是使用 def 关键词,异步的函数,协程函数(Coroutine)本质上是一个函数,特点是在代码块中可以将执行权交给其他协程,使用async def 来定义。

# 普通函数定义
def add(x):
    print(x+2)
    return x+2

# 异步函数的定义
async def add(x):
    print("in async fun add")
    return x+3

如何调用协程并且得到它的运行结果? 调用普通的函数只需要 result = add2(2),这时函数就可以得到运行,并且将结果4返回给result,如果使用result = add(2),此时再打印 result 呢?得到的是一个coroutine对象,,并不是2+3=5这个结果,怎样才能得到结果呢?协程函数想要执行需要放到事件循环里执行。

import asyncio

async def add(x):
    print("in async fun add")
    return x+3

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
result = loop.run_until_complete(add(2))
print(result)

运行结果:

in async fun add
5

Eventloop 是asyncio应用的核心,把一些异步函数注册到这个事件循环上,事件循环会循环执行这些函数,当执行到某个函数时,如果它正在等待I/O返回,如它正在进行网络请求,或者sleep操作,事件循环会暂停它的执行去执行其他的函数;当某个函数完成I/O后会恢复,下次循环到它的时候继续执行。因此,这些异步函数可以协同(Cooperative)运行:这就是事件循环的目标。

或者使用await 关键字来修饰函数的调用,如result = await add3(2),但是await只能用在协程函数中,通俗讲就是await与async要成对出现,所以想要用await关键字就还需要定义一个协程函数,并且最终的执行还是需要放到一个事件循环中进行。


import asyncio

async def add(x):
    print("in async fun add")
    return x+3


async def main():
    result = await add(2)
    return result

if __name__ == '__main__':
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    result = loop.run_until_complete(main())
    print(result)

    #或者
    '''
    asyncio.run是Python 3.7后新添加的接口,大大简化了上面的写法。
    result = asyncio.run(main())
    print(result)
    '''

运行结果:

in async fun add
5

2.实例

我们用asyncio的异步网络连接来获取baidu、sohu和163的网站首页。

import asyncio


async def wget(host):
    print('wget %s...' % host)
    reader, writer = await asyncio.open_connection(host, 80)
    header = 'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % host
    writer.write(header.encode('utf-8'))
    await writer.drain()
    while True:
        line = await reader.readline()
        if line == b'\r\n':
            break
        print('%s header > %s' % (host, line.decode('utf-8').rstrip()))
    # Ignore the body, close the socket
    writer.close()


loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
tasks = [wget(host) for host in ['www.baidu.com.cn', 'www.sohu.com', 'www.163.com']]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

运行结果:

wget www.sohu.com...
wget www.163.com...
wget www.baidu.com.cn...
www.sohu.com header > HTTP/1.1 307 Temporary Redirect
www.sohu.com header > Content-Type: text/html
www.sohu.com header > Content-Length: 180
www.sohu.com header > Connection: close
www.sohu.com header > Server: nginx
www.sohu.com header > Date: Sun, 15 May 2022 10:32:57 GMT
www.sohu.com header > Location: https://www.sohu.com/
www.sohu.com header > FSS-Cache: from 4336953.6237507.6111062
www.sohu.com header > FSS-Proxy: Powered by 3419435.4402485.5193530
www.163.com header > HTTP/1.1 301 Moved Permanently
www.163.com header > Date: Sun, 15 May 2022 10:32:56 GMT
www.163.com header > Content-Length: 0
www.163.com header > Connection: close
www.163.com header > Server: web cache
www.163.com header > Location: https://www.163.com/
www.163.com header > Cache-Control: no-cache,no-store,private
www.163.com header > cdn-user-ip: 61.185.195.205
www.163.com header > cdn-ip: 1.180.18.26
www.163.com header > X-Cache-Remote: MISS
www.163.com header > cdn-source: baishan
www.baidu.com.cn header > HTTP/1.0 302 Found
www.baidu.com.cn header > Location: http://www.baidu.com/
www.baidu.com.cn header > Date: Sun, 15 May 2022 10:32:57 GMT
www.baidu.com.cn header > Content-Length: 44
www.baidu.com.cn header > Content-Type: text/html; charset=utf-8

小结:

协程要用 asyncdef声明,Python 3.5时的装饰器写法(@asyncio.coroutine)已经过时,Eventloop可以说是asyncio应用的核心,是中央总控。Eventloop实例提供了注册、取消和执行任务和回调的方法。