asyncio.sleep() 必须 await 才生效,否则仅创建协程对象无延迟;time.sleep() 会阻塞整个事件循环,破坏异步并发性,正确做法是用 run_in_executor 调用阻塞函数。

不能混用,本质是线程控制权归属不同:一个是让出协程执行权,一个是锁死整条线程。
asyncio.sleep() 必须 await,否则什么也不会发生
它返回的是一个 coroutine 对象,不是立即执行的函数。漏写 await 时,你只是创建了一个待执行的协程,但事件循环根本不会调度它——程序会“秒过”,毫无延迟。
-
asyncio.sleep(1)不加await→ 返回<coroutine object asyncio.sleep at></coroutine>,无休眠效果 - 在普通函数(非
async def)里调用 → 触发RuntimeWarning: coroutine 'sleep' was never awaited - 在没运行事件循环的环境(如直接脚本顶层、REPL)中调用 → 报
RuntimeError: no running event loop
time.sleep() 一调用就卡死整个 asyncio 程序
它调用的是操作系统级休眠(比如 nanosleep),当前线程立刻冻结。而 asyncio 的事件循环就跑在这条线程上——结果就是:所有协程停摆、IO 就绪检查中断、定时器失效、新任务无法启动。
- 哪怕只写
time.sleep(0.001),也会阻塞至少一个完整调度周期,可能跳过多个已就绪的协程 - 常见误用位置:日志重试逻辑里写
while not ok: send(); time.sleep(0.5)、async for循环体中插time.sleep(0.1)、用requests.get()时顺手加time.sleep()控频 - 现象:并发任务退化成串行,总耗时 = 各任务
time.sleep()时间之和
并发等待行为完全不同
多个 asyncio.sleep() 可以真正并发等待;time.sleep() 则永远只能串行执行。
- 并行写法:
await asyncio.gather(asyncio.sleep(1), asyncio.sleep(2))→ 总耗时 ≈ 2 秒 - 串行写法:
await asyncio.sleep(1); await asyncio.sleep(2)→ 总耗时 ≈ 3 秒 - 若错误替换成
time.sleep(1); time.sleep(2)→ 即便在async def里,也强制串行,总耗时 ≈ 3 秒,且期间事件循环完全停滞
真要调同步阻塞函数,别硬塞 time.sleep()
遇到必须用的同步代码(比如老 SDK、本地计算、subprocess.run()),正确做法是剥离到线程池,而不是靠 time.sleep() 掩盖问题。
- IO 类阻塞:用
loop.run_in_executor(None, blocking_func, *args) - 需控并发数:传
ThreadPoolExecutor(max_workers=3),避免默认池挤占资源 - CPU 密集型:换
ProcessPoolExecutor,否则线程池在 GIL 下反而加剧竞争 - 绝对不要在
run_in_executor里传lambda或闭包引用大对象,容易内存泄漏
最麻烦的从来不是分不清该用哪个 sleep,而是把 time.sleep() 当成万能延时工具,嵌在异步上下文里还浑然不觉——它不报错,但悄悄拖垮整个并发模型。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











