asyncio模块如何工作,为什么我的更新样本同步运行?

时间:2017-07-26 07:11:43

标签: python async-await python-asyncio

我在Python 3.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

它似乎没有以异步模式运行,而是以正常的同步模式运行。

问题是:为什么它没有以异步模式运行?如何将旧的同步模块更改为“异步”模式?一个?

1 个答案:

答案 0 :(得分:8)

Asyncio使用事件循环,它选择队列中的任务(协同程序的独立调用链)以便下次激活。事件循环可以做出关于哪些任务准备好进行实际工作的明智决策。这就是事件循环还负责creating connectionswatching file descriptors以及其他I / O原语的原因;它使事件循环能够深入了解正在进行的I / O操作或何时可以处理结果。

每当您使用await时,都有机会将控制权返回给循环,然后可以将控制权传递给另一个任务。然后选择执行哪个任务取决于具体的实现; asyncio参考实现offers multiple choices,但还有其他实现,例如效率非常高的uvloop implementation

您的示例仍然是异步的。只是通过用同步await.sleep()调用替换time.sleep(),在新的协程函数中,你将2个协同程序引入任务调用链中而不会产生,从而影响它们的顺序执行。它们以看似同步的顺序执行是巧合。如果你切换了事件循环,或者引入了更多的协程(特别是那些使用I / O的协程),那么订单可能会再次变得不同。

此外,您的新协程使用time.sleep();这使你的协同程序不合作。事件循环未通知您的代码正在等待(time.sleep()不会产生!),因此在time.sleep()运行时无法执行其他协程time.sleep()只是不会返回或让任何其他代码运行,直到所请求的时间已过。将其与asyncio.sleep() implementation进行对比,call_later() hook只会产生asyncio: why isn't it non-blocking by default的事件循环;事件循环现在知道该任务直到稍后才需要注意。

另请参阅executor pool以深入讨论任务和事件循环如何交互。如果您必须运行无法合作的阻止,同步代码,请使用{{3}}在单独的步骤或子进程中执行阻止代码以释放事件循环用于其他更好的任务。