如何无需asyncio.sleep(0),用asyncio.create_task()立即启动任务?
问题:asyncio创建任务后无需显式
asyncio.sleep(0)即可立即启动的方案? 代码示例
import asyncio import time async def coro_1(seconds=5): await asyncio.sleep(seconds) async def main_1(): task_1 = asyncio.create_task(coro_1()) # 此处为长时间计算 time.sleep(5) await task_1 async def main_2(): task_1 = asyncio.create_task(coro_1()) await asyncio.sleep(0) # 此处为长时间计算 time.sleep(5) await task_1 if __name__ == '__main__': start = time.time() asyncio.run(main_1()) end = time.time() print(f'Main_1 took { end - start } seconds.') start = time.time() asyncio.run(main_2()) end = time.time() print(f'Main_2 took { end - start } seconds.')
输出结果
Main_1 took 10.005882263183594 seconds. Main_2 took 5.005404233932495 seconds.
问题分析
main_1耗时更长是因为time.sleep()是同步阻塞操作,会卡住事件循环,导致coro_1任务必须等到await task_1时才会被调度启动;而main_2通过await asyncio.sleep(0)主动让出执行权,让事件循环有机会调度启动coro_1,因此两者可以并发执行,总耗时缩短。
现在需要寻找无需显式添加asyncio.sleep(0)的更优方案,让任务创建后立即启动,避免冗余代码。
解决方案
1. 将同步阻塞任务放入线程池执行
asyncio的事件循环是单线程的,同步阻塞代码会占用事件循环线程,导致其他任务无法被调度。使用asyncio.get_running_loop().run_in_executor()可以把阻塞任务放到线程池中执行,这样事件循环不会被卡住,创建的任务会立即被调度启动。
修改后的main_1代码:
async def main_1(): task_1 = asyncio.create_task(coro_1()) # 将阻塞的sleep(或其他长时间计算)放入线程池 await asyncio.get_running_loop().run_in_executor(None, time.sleep, 5) await task_1
这样运行后,main_1的耗时会和main_2接近,无需添加asyncio.sleep(0)。
2. 用异步替代同步阻塞操作
如果你的长时间计算可以改写成异步非阻塞的形式(比如使用支持异步的库),直接用异步操作代替同步阻塞代码,事件循环自然会在合适的时机调度其他任务。
3. 手动触发事件循环调度(不推荐)
虽然可以用asyncio.get_running_loop().stop()和asyncio.get_running_loop().run_forever()手动触发调度,但这种方式复杂度高,容易引发问题,不如线程池方案简洁可靠。
内容的提问来源于stack exchange,提问作者Selnay
相关产品推荐
相关产品推荐

