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

似乎它不是在异步模式下运行,而是在正常的同步模式下运行。

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


问题答案:

Asyncio使用 事件循环 ,该 循环 选择队列中要激活的 任务
(协程的独立调用链)。事件循环可以明智地决定准备执行实际任务的任务。这就是为什么事件循环还负责创建连接监视文件描述符和其他I / O原语的原因。它使事件循环可以洞悉何时正在进行I /
O操作或何时可以处理结果。

无论何时使用await,都有机会将控制权返回到循环,然后可以将控制权传递给另一个任务。然后选择执行哪个任务取决于确切的实现。该asyncio参考实现提供了多种选择,但也有其他的实现,如非常,非常有效uvloop实现

您的示例 仍然异步的
。碰巧的是,通过在新的协程函数内部await.sleep()用同步time.sleep()调用替换,您在任务调用链中引入了两个不屈服的协程,从而影响了它们执行的顺序。以似乎同步的顺序执行它们是一个
巧合 。如果您切换了事件循环,或引入了更多的协程(特别是一些使用I / O的协程),则顺序很容易再次不同。

而且,您的新协程使用time.sleep();这会使你的协程 不合作
。不会通知事件循环您的代码正在等待(time.sleep()不会让步!),因此在运行时
无法执行其他协程time.sleep()time.sleep()只是在请求的时间过去之后才 返回或让其他任何代码运行
。将此与asyncio.sleep()实现进行对比,该实现只是通过call_later()钩子产生了事件循环;
现在,事件循环知道该任务直到以后才需要注意。

另请参见asyncio:默认情况下为什么不进行非阻塞以更深入地讨论任务和事件循环的交互方式。而且,如果必须运行无法协作的
阻塞,同步代码 ,则可以使用执行程序池在单独的踏步或子进程中执行阻塞代码,以将事件循环释放给其他性能更好的任务。