我在Python3.6中为asyncio尝试了以下代码:
例1:
import asyncio
import time
async def hello():
print('hello')
await asyncio.sleep(1)
print('hello again')
tasks=[hello(),hello()]
loop=asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
产出如预期:
hello
hello
hello again
hello again
然后我想将asyncio.sleep更改为另一个def:
async def sleep():
time.sleep(1)
async def hello():
print('hello')
await sleep()
print('hello again')
tasks=[hello(),hello()]
loop=asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
输出:
hello
hello again
hello
hello again
它似乎不是在异步模式下运行,而是在正常的同步模式下运行。
问题是:为什么它不在异步模式下运行,我如何将旧的同步模块更改为“异步”模块?
最佳答案
Asyncio使用一个事件循环,它选择队列中接下来要激活的任务(一个独立的协同路由调用链)。事件循环可以智能地决定哪些任务可以完成实际工作。这就是为什么事件循环还负责creating connections和watching file descriptors以及其他I/O原语;它让事件循环了解何时有正在进行的I/O操作,或者何时有结果可供处理。
无论何时使用await
,都有机会将控制权返回到循环,然后循环可以将控制权传递给另一个任务。然后选择哪个任务执行取决于具体的实现;asyncio
引用实现offers multiple choices,但还有其他实现,例如非常高效的uvloop implementation。
您的示例仍然是异步的。碰巧,在一个新的协程函数中,通过用一个同步的调用替换await.sleep()
调用,您在任务调用链中引入了两个不屈服的协程,从而影响了它们的执行顺序。它们的执行顺序似乎是同步的,这是一个巧合。如果您切换了事件循环,或者引入了更多的协程(特别是一些使用I/O的协程),那么顺序很容易再次不同。
此外,你的新协程使用time.sleep()
,这使你的协程不合作。不会通知事件循环您的代码正在等待(time.sleep()
将不会产生!),因此在运行time.sleep()
时不能执行其他协同程序。time.sleep()
只是在请求的时间过去之前不返回或让任何其他代码运行。与此相反的是time.sleep()
implementation,它只会产生带有asyncio.sleep()
hook的事件循环;事件循环现在知道该任务在以后不需要任何关注。
有关任务和事件循环如何交互的更深入讨论,请参见asyncio: why isn't it non-blocking by default。如果必须运行无法协作的阻塞同步代码,则使用executor pool将阻塞代码在单独的线程或子进程中执行,以释放事件循环以执行其他性能更好的任务。