Python 异步 wait_for 同步

Python asyncio wait_for synchronous

使用 Python 3.6.8

async def sleeper():
    time.sleep(2)

async def asyncio_sleeper():
    await asyncio.sleep(2)

await asyncio.wait_for(sleeper(), 1)

await asyncio.wait_for(asyncio_sleeper(), 1)

使用 time.sleep 不会超时,asyncio.sleep 会超时。

我的直觉是,在协程上调用 wait_for 会根据协程花费多长时间来超时,而不是根据协程中的各个异步调用。 导致这种行为的幕后发生了什么,是否有办法修改行为以符合我的直觉?

What is going on behind the scenes that results in this behavior

最简单的答案是asyncio是基于合作多任务处理,time.sleep不合作。 time.sleep(2) 阻塞线程两秒,事件循环等等,任何人都无能为力。

另一方面,asyncio.sleep很小心written,所以当你await asyncio.sleep(2)时,它会立即挂起当前任务并安排事件循环在2秒后恢复它. Asyncio 的“休眠”是隐式的,它允许事件循环在协程挂起时继续执行其他任务。相同的挂起系统允许 wait_for 取消任务,事件循环通过在等待 await 中引发异常的情况下“恢复”它来完成任务。

一般来说,不等待任何事情的协同程序很好地表明它的编写不正确,并且只是名义上的协同程序。等待是协同程序存在的原因,sleeper 不包含任何。

is there a way to modify behavior to match my intuition?

如果您必须从 asyncio 调用遗留阻塞代码,请使用 run_in_executor。当你这样做时,你必须告诉 asyncio 并允许它执行实际的阻塞调用,如下所示:

async def sleeper():
    loop = asyncio.get_event_loop()
    await loop.run_in_executor(None, time.sleep, 2)

time.sleep(或其他阻塞函数)将被移交给一个单独的线程,sleeper 将被挂起,在 time.sleep 完成后恢复。与 asyncio.sleep() 不同的是,阻塞 time.sleep(2) 仍会被调用并阻塞其线程 2 秒,但这不会影响事件循环,它的工作方式与 [=] 时类似使用了 24=]。

请注意,取消等待 run_in_executor 的协程只会取消在另一个线程中等待阻塞 time.sleep(2) 完成。阻塞调用将继续执行直到完成,这是可以预料的,因为没有中断它的通用机制。