TLDR:不要在协程中使用阻塞调用,例如time.sleep。使用asyncio.sleep 异步暂停,如果必须运行阻塞代码,则使用an event loop executor。
使用asyncio.wait([thing]) 会增加一个间接级别,在新的Future/Task 中执行thing。虽然裸 await asyncio.sleep(5) 在 coro1 期间执行睡眠,但包装的 await asyncio.wait([asyncio.sleep(5)]) 执行睡眠毕竟其他当前调度的协程。
async def coro1():
logger.info("coro1 start")
await asyncio.sleep(5) # started immediately
logger.info("coro1 finish")
async def coro1():
logger.info("coro1 start")
await asyncio.wait([ # started immediately
asyncio.sleep(5) # started in new task
])
logger.info("coro1 finish")
由于coro2 使用阻塞time.sleep(10),它会禁用事件循环和所有其他协程。
async def coro2():
logger.info("coro2 start")
time.sleep(10) # nothing happens for 10 seconds
logger.info("coro2 finish")
这会阻止更多的 Futures 被启动 - 包括来自 asyncio.wait 的新未来 - 以及被恢复 - 包括裸露的 asyncio.sleep(5)。在前一种情况下,这意味着 asyncio.sleep 在 time.sleep 完成之后开始 - 因此需要 10 + 5 秒才能完成。在后一种情况下,这意味着 asyncio.sleep 已经开始,它只是无法在 10 秒之前完成 - 因此需要 max(10, 5) 秒才能完成。
始终使用asyncio.sleep 来获得所需的持续时间。如果必须执行阻塞代码,请使用run via an executor。
async def coro1w():
print("coro1w start", time.asctime())
await asyncio.wait([asyncio.sleep(5)])
print("coro1w finish", time.asctime())
async def coro1b():
print("coro1b start", time.asctime())
await asyncio.sleep(5)
print("coro1b finish", time.asctime())
async def coro2a():
print("coro2a start", time.asctime())
await asyncio.sleep(10) # asynchronous sleep
print("coro2a finish", time.asctime())
async def coro2t():
print("coro2t start", time.asctime())
loop = asyncio.get_running_loop() # threaded sleep
await loop.run_in_executor(None, lambda: time.sleep(10))
print("coro2t finish", time.asctime())
async def main():
await asyncio.gather(coro1w(), coro1b(), coro2a(), coro2t())
asyncio.run(main())