核心结论

asyncio.sleep(delay)异步阻塞不卡死事件循环time.sleep(delay)同步阻塞直接堵死整个事件循环,二者底层等待逻辑、对协程的影响完全不同。


一、基础对比

对比项 asyncio.sleep(delay) time.sleep(delay)
所属模块 异步库 asyncio 标准同步库 time
调用方式 必须搭配 await 直接调用,不能加 await
阻塞性质 协程级阻塞 线程级阻塞
执行时行为 协程挂起,释放事件循环线程,可调度其他协程 整条线程卡死,不释放执行权,事件循环停滞
适用场景 协程/异步代码内部 普通同步代码

二、逐点详解 + 代码演示

1. asyncio.sleep(异步休眠,协程友好)

import asyncio

async def task(name, t):
    print(f"任务{name} 开始")
    await asyncio.sleep(t)  # 异步等待
    print(f"任务{name} 结束")

async def main():
    t1 = asyncio.create_task(task("A", 1))
    t2 = asyncio.create_task(task("B", 2))
    await t1
    await t2

asyncio.run(main())

运行时序:

  1. A、B 先后启动;
  2. A 执行到 asyncio.sleep(1)当前协程挂起,线程还给事件循环
  3. 循环切换执行 B;
  4. B 执行到 asyncio.sleep(2) → 同样挂起;
  5. 1秒后 A 唤醒执行收尾,2秒后 B 唤醒收尾;
    总耗时 ≈ 2秒,正常并发。

本质:模拟异步IO等待,只是让出协程执行权,不占用线程,事件循环可以正常调度其他任务。


2. time.sleep(同步休眠,阻塞整个线程)

import asyncio
import time

async def task(name, t):
    print(f"任务{name} 开始")
    time.sleep(t)  # 同步休眠
    print(f"任务{name} 结束")

async def main():
    t1 = asyncio.create_task(task("A", 1))
    t2 = asyncio.create_task(task("B", 2))
    await t1
    await t2

asyncio.run(main())

运行时序:

  1. A 启动,执行 time.sleep(1)整个事件循环线程被卡住
  2. 必须等1秒休眠结束,A 才会收尾;
  3. 之后才轮到 B 执行、休眠2秒;
    总耗时 ≈ 3秒,完全退化成串行,并发失效。

本质:它是同步阻塞函数,会牢牢占用当前线程,不触发协程挂起、不释放执行权,事件循环无法切换任务。


三、关键补充知识点

  1. 语法限制

    • asyncio.sleep 返回可等待对象,异步代码里必须写 await,否则只是创建对象,不会等待;
    • time.sleep 是普通同步函数,不能加 await,加了会报错。
  2. 异步代码中想用 time.sleep 怎么办?
    asyncio.to_thread 把同步休眠丢到子线程执行,避免阻塞事件循环:

    # 安全写法
    await asyncio.to_thread(time.sleep, 1)
    
  3. 底层等待逻辑
    两者最终都是调用操作系统内核完成“延时等待”,等待期间都不消耗CPU
    区别只在于:是否释放事件循环线程、是否允许任务切换


四、使用选择

  1. 纯异步协程代码里做延时:优先用 await asyncio.sleep(),保证并发;
  2. 同步代码、普通线程里做延时:time.sleep()
  3. 异步环境中必须调用 time.sleep/其他同步阻塞函数:套一层 asyncio.to_thread

一句话速记

asyncio.sleep 睡觉让出位置,别人可以干活;
time.sleep 睡觉占着工位,所有人都得等着。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐