Python中的asyncio是一个使用事件循环驱动的协程实现高并发的标准库。下面简单记录asyncio的学习心得。
本文知识点速览:
创建一个协程
async def 句法定义一个协程函数coro,调用协程函数返回一个协程对象。
即coro -->协程函数;coro() -->协程对象。
运行一个协程
1.asyncio.run()
:只应在最外层调用主协程main()时使用。
2.在一个外层协程函数中使用await语句运行协程。
3.使用asyncio.create_task()
或asyncio.ensure_future()
将协程包装在一个Task对象中,python 会自动schedule该协程的执行。
多协程并发
awaitable:协程、Task、Future
1.asyncio.gather()
:接受一堆awaitable作为可变参数(传入协程会自动包装成Task),并发执行所有awaitable。返回的是一个Future。
执行该Future对象:
可以传入loop.run_until_completed()
执行;
也可以在外层协程函数中被await执行;
也可以包装在一个外层main()协程中通过asyncio.run()执行。
获取结果:
Future对象执行完毕后返回一个二元素tuple,从中可提取到一堆Task对象,调用task.result()
获取返回值,返回值的顺序与传入的awaitable顺序相关。
2.asyncio.wait()
:接受一个包含awaitable的列表(其中的协程会自动包装为Task),并发执行所有awaitable。返回的是一个协程。
协程的运行不用多说了吧。。。
获取结果:
该协程对象直接返回一个包含了每个awaitable返回值的列表。返回值的顺序与传入的awaitable顺序相关。
3.asyncio.as_completed()
:接受一个包含awaitable的列表,返回的是一个生成器,产出Future。
Each Future object returned represents the earliest result from the set of the remaining awaitables.
参照文档,运行和获取结果的标准用法如下:
async def main():
results = []
awaitable_list = [awaitable_work(x, y) for x, y in zip((1, 2, 3), 'ABC')]
for future in asyncio.as_completed(awaitable_list):
result = await future
results.append(result)
return results
print('Results:', asyncio.run(main()))
##########################################################################################
正文开始:
定义一个协程函数
asyncio文档中特别强调要区分协程函数和协程对象的区别:
a coroutine function: an async def function;
a coroutine object: an object returned by calling a coroutine function.
通过async def 句法定义一个协程函数,调用该函数获得协程对象。
下文中所有“协程”仅指协程对象。
async def awaitable_work(time, name):
print('Enter {}...will sleep {}s'.format(name, time))
await asyncio.sleep(time)
print('Done {}...for {}s.'.format(name, time))
asyncio.iscoroutine(awaitable_work(1, 'A') # True
asyncio.iscoroutinefunction(awaitable_work) # True
协程无法直接执行。
执行一个协程有三种基本方法:
第一种方法
asyncio.run(coro, *, debug=False)
,仅接收协程对象作为参数,它实际上也是通过event loop来执行。调用该函数会创建一个新的event loop并在执行完毕后关闭该loop。如果当前线程已经有一个运行中的event loop,该函数无法执行。根据文档建议,这种方法仅应用于在最外层代码中启动主协程。
第二种方法
在一个外层协程函数的定义体中,通过await
语句执行协程。
(ps:await语句也只能在协程的函数体中使用)。await语句是生成器中yield from 的代替品。
在外层协程中执行x = await y,如果y是一个协程,相当于y获得执行权。直到y全部执行完毕,外层协程取回执行权,将y的返回值赋给x。
async def outter_coro():
await awaitable_work(2, 'B'))
await awaitable_work(4, 'C'))
asyncio.run(outter_coro())
这里await的两个协程执行是线性的,共计执行6s,非并发。
第三种方法
使用asyncio.create_task(coro)
,根据文档的描述:The asyncio.create_task() function to run coroutines concurrently as asyncio Tasks.
包装成为task对象之后,coro的执行是并发的。
先来看一下asyncio.create_task的源码,很简单