asyncio异步IO--协程(Coroutine)与任务(Task)详解

摘要:本文翻译自,主要介绍asyncio中用于处理协程和任务的方法和接口。在翻译过程中,译者在官方文档的基础上增加了部分样例代码和示意图表,以帮助读者对文档的理解。本文内容主要针对python3.7,在低版本的python中可能不适用,敬请留意。原创内容,如需转载请注明出处。
译者:马鸣谦(邮箱:553850687@qq.com)

协程

协程(coroutines)是通过async/await定义函数或方法,是使用asyncio进行异步编程的首选途径。如下,是一个协程的例子:

import asyncio async def main(): print("hello") await asyncio.sleep(1) print("world")

上例中的 main 方法就是我们定义的协程 。代码的功能很简单:

s=>start: 开始 op1=>operation: 打印“hello” op2=>operation: 沉睡1秒 op3=>operation: 打印“world” e=>end: 结束 s(right)->op1->op2->op3(right)->e

我们在交互环境(Python3.7)下执行以上代码,看看效果:

>>> import asyncio >>> async def main(): ... print("hello") ... await asyncio.sleep(1) ... print("world") >>> asyncio.run(main()) hello world

需要注意的是:如果像执行普通代码一样直接调用main(),只会返回一个coroutine对象,main()方法内的代码不会执行:

>>> main() #直接执行`main()`返回的是一个`coroutine对象`。 <coroutine object main at 0x0000000002C97848>

实际上,asyncio提供了三种执行协程的机制:

使用asyncio.run()执行协程。一般用于执行最顶层的入口函数,如main()。

await一个协程。一般用于在一个协程中调用另一协程。 如下是一个示例:

>>> import time >>> async def say_after(delay,what): await asyncio.sleep(delay) print(what) >>> async def main(): print(f"started at {time.strftime('%X')}") await say_after(1,"hello") await say_after(2,"world") print(f"finished at {time.strftime('%X')}") >>> asyncio.run(main()) started at 16:47:10 hello world finished at 16:47:13

执行耗时 3秒

用asyncio.create_task()方法将Coroutine(协程)封装为Task(任务)。一般用于实现异步并发操作。 需要注意的是,只有在当前线程存在事件循环的时候才能创建任务(Task)。

我们修改以上的例程,并发执行 两个say_after协程。

async def main(): task1 = asyncio.create_task(say_after(1,"hello")) task2 = asyncio.create_task(say_after(2,"world")) print(f"started at {time.strftime('%X')}") await task1 await task2 print(f"finished at {time.strftime('%X')}")

执行asyncio.run(main()),结果如下:

started at 17:01:34 hello world finished at 17:01:36

耗时2秒

“可等待”对象(Awaitables)

如果一个对象能够被用在await表达式中,那么我们称这个对象是可等待对象(awaitable object)。很多asyncio API都被设计成了可等待的。
主要有三类可等待对象:

协程coroutine

任务Task

未来对象Future。

Coroutine(协程)

Python的协程是可等待的(awaitable),因此能够被其他协程用在await表达式中。

import asyncio async def nested(): print("something") async def main(): # 如果直接调用 "nested()",什么都不会发生. # 直接调用的时候只是创建了一个 协程对象 ,但这个对象没有被 await, # 所以它并不会执行. nested() # 那么我们 await 这个协程,看看会是什么结果: await nested() # 将会打印 "something". asyncio.run(main())

重要:在这篇文章中,术语coroutine或协程指代两个关系紧密的概念:

协程函数(coroutine function):由async def定义的函数;

协程对象(coroutine object):调用 协程函数返回的对象。

asyncio也支持传统的基于生成器的协程。

Task(任务)

Task用来 并发的 调度协程。
当一个协程通过类似 asyncio.create_task() 的函数被封装进一个 Task时,这个协程 会很快被自动调度执行:

import asyncio async def nested(): return 42 async def main(): # Schedule nested() to run soon concurrently # with "main()". task = asyncio.create_task(nested()) # "task" can now be used to cancel "nested()", or # can simply be awaited to wait until it is complete: await task asyncio.run(main()) Future(未来对象)

Future 是一种特殊的 底层 可等待对象,代表一个异步操作的最终结果
当一个Future对象被await的时候,表示当前的协程会持续等待,直到 Future对象所指向的异步操作执行完毕。
在asyncio中,Future对象能使基于回调的代码被用于asyn/await表达式中。
一般情况下,在应用层编程中,没有必要 创建Future对象。
有时候,有些Future对象会被一些库和asyncio API暴露出来,我们可以await它们:

async def main(): await function_that_returns_a_future_object() # this is also valid: await asyncio.gather( function_that_returns_a_future_object(), some_python_coroutine() )

底层函数返回Future对象的一个例子是:

执行asyncio程序 asyncio.run(coro, * , debug=False)

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyzspx.html