我试图正确理解和实现两个并发运行的 Task
对象,使用 Python 3相对较新的 asyncio
模块。
简而言之,异步似乎被设计用于在事件循环中处理异步进程和并发 Task
执行。它提倡使用 await
(应用于异步函数)作为等待和使用结果的无回调方式,而不会阻塞事件循环。(期货和回购仍是一个可行的选择。)
它还提供了 asyncio.Task()
类,这是 Future
的一个专门的子类,用于包装协程。最好通过使用 asyncio.ensure_future()
方法调用。异步任务的预期用途是允许独立运行的任务与同一事件循环中的其他任务“并发”运行。我的理解是,Tasks
连接到事件循环,然后自动继续驱动 await
语句之间的协程。
我喜欢能够使用并发任务而不需要使用其中一个 Executor
类的想法,但是我还没有找到关于实现的详细说明。
我现在是这么做的:
import asyncio
print('running async test')
async def say_boo():
i = 0
while True:
await asyncio.sleep(0)
print('...boo {0}'.format(i))
i += 1
async def say_baa():
i = 0
while True:
await asyncio.sleep(0)
print('...baa {0}'.format(i))
i += 1
# wrap in Task object
# -> automatically attaches to event loop and executes
boo = asyncio.ensure_future(say_boo())
baa = asyncio.ensure_future(say_baa())
loop = asyncio.get_event_loop()
loop.run_forever()
在尝试同时运行两个循环任务的情况下,我注意到除非 Task 有一个内部的 await
表达式,否则它会卡在 while
循环中,有效地阻止其他任务的运行(很像一个正常的 while
循环)。但是,当 Tasks 必须(a)等待时,它们似乎可以并发运行而不会出现问题。
因此,await
语句似乎为事件循环提供了在任务之间来回切换的立足点,从而产生了并发效果。
内部 await
的输出示例:
running async test
...boo 0
...baa 0
...boo 1
...baa 1
...boo 2
...baa 2
示例输出 没有内部 await
:
...boo 0
...boo 1
...boo 2
...boo 3
...boo 4
这个实现是否通过了 asyncio
中并发循环任务的“正确”示例?
Task
提供一个阻塞点(await
表达式)以便事件循环处理多个任务,这样的唯一方法是正确的吗?
2022年更新: 请注意,自从这个问题被提出以来,asyncio
API 已经发生了相当大的变化。请参阅新标记为正确答案的部分,该部分现在显示了 Python 3.10中 API 的正确使用。我仍然推荐@dano 的答案,以便更广泛地了解这是如何在引擎盖下工作的。