📢 欢迎来到万事技术论坛!本站仅讨论合法编程技术话题,严禁外挂/作弊/黑产/盗版内容,违者封号。

精华asyncio 踩坑记录:明明写了 async 却一点没变快

lin_dev 活跃会员

第一次用 asyncio 十有八九会写成这样,然后发现耗时和不写异步一模一样:

import asyncio, time

async def fetch(x):
    time.sleep(1)      # ← 问题在这
    return x * 2

async def main():
    return await asyncio.gather(*[fetch(i) for i in range(5)])

asyncio.run(main())    # 耗时 5 秒,不是 1 秒

原因

time.sleep() 是阻塞调用,它会把整个线程(也就是整个事件循环)卡住。asyncio 是单线程协程,靠的是「遇到 IO 就让出控制权」来实现并发,你在协程里放一个阻塞函数,让都让不出去,自然退化成串行。

正确写法

把阻塞调用换成对应的异步版本:

async def fetch(x):
    await asyncio.sleep(1)     # 异步版
    return x * 2

网络请求同理:requests 是阻塞的,要用 aiohttp / httpx.AsyncClient。

真没异步库怎么办

比如要调用一个只有同步接口的 SDK,丢到线程池里跑:

result = await asyncio.to_thread(blocking_func, arg)   # Python 3.9+

第二个坑:忘了 await

async def main():
    fetch(1)        # 只是创建了协程对象,根本没执行

这种不会报错,日志里一片安静,是最难查的一类。开个 lint 规则或者直接用 asyncio.gather 包起来就好很多。

一句话总结:async 只是声明,并发靠的是不阻塞 + await 让出。

楼主 · 2026-09-28 13:21 · 浏览 22
ops_wang 进阶会员

补充一个:用 asyncio.run() 包住 main 时,如果在协程外还起了别的 loop 会报 "RuntimeError: This event loop is already running",Jupyter 里尤其常见,那种环境直接用 await 就行。

1楼 · 2026-09-28 13:21
yuki 进阶会员

asyncio.to_thread 这个太实用了,之前有个项目要调一个只有同步接口的 OCR SDK,一直不知道怎么塞进异步链路,试了下确实解决了。

2楼 · 2026-09-28 13:21
登录 后即可参与回复。