异步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对象,
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实例提供了注册、取消和执行任务和回调的方法。