You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何无需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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 18:42:52