bigywater.tech
Python · 3

asyncio 为什么会阻塞:一个同步函数拖慢整个事件循环

让1个任务用time.sleep(0.5)阻塞,另外3个任务只需要asyncio.sleep(0.1),验证同步阻塞调用是怎么拖慢整个事件循环上所有任务的。

0.1s→0.6s
被同步阻塞拖累的任务完成时间
0拖累
对照组用asyncio.sleep时完全不受影响
单线程共享
所有任务挤在同一个线程里执行

做的事:4个任务一起跑,1个用time.sleep,3个用asyncio.sleep

同一个事件循环里,混入一个的同步阻塞调用。

代码async def blocking_worker(i): time.sleep(0.5) # 的同步阻塞调用,不是await return i async def async_worker(i): await asyncio.sleep(0.1) # 的异步等待 return i await asyncio.gather( blocking_worker(0), async_worker(1), async_worker(2), async_worker(3), )

task1、task2、task3本身只需要等0.1秒——按理说应该在0.1秒左右就完成。但它们跟task0(需要卡住0.5秒的同步调用)在同一个事件循环里跑。

验证:只需要0.1秒的任务,被拖到了0.6秒

统一从gather开始计时,看每个任务完成的时间点。

结果(task0用time.sleep阻塞)总耗时: 0.6063s task0 在 0.5049s 完成 task1 在 0.6062s 完成 ← 本来只要0.1s,被拖到0.6s! task2 在 0.6062s 完成 task3 在 0.6062s 完成 结果(对照组:全部用asyncio.sleep)总耗时: 0.5008s task0 在 0.5008s 完成 task1 在 0.1011s 完成 ← 还是原本的0.1s task2 在 0.1011s 完成 task3 在 0.1011s 完成

原因:time.sleep() 是一个真正的同步阻塞调用,它会占住唯一的那个线程整整0.5秒——在这0.5秒里,事件循环压根没有机会去推进task1、task2、task3的执行,哪怕它们只需要0.1秒就能跑完。等task0的同步调用结束、把控制权交还给事件循环,task1-3才有机会开始跑它们的0.1秒等待,所以它们的完成时间被推到了0.5+0.1=0.6秒左右。

这是asyncio里最容易踩的坑:一个async函数里用了同步阻塞调用(time.sleep、requests.get、同步数据库查询等),会拖慢整个事件循环上的所有任务,不只是拖慢自己。对照组里全部换成asyncio.sleep后,task1-3完全不受task0影响,该多快还是多快——因为asyncio.sleep会把控制权交还给事件循环,而不是死死攥住线程不放。

分步演示:一个time.sleep是怎么拖慢别的任务的

每一步都是本文上面用Python 3.14跑出来的原始结果。

一个同步阻塞调用,拖慢了整个事件循环
这一步新验证的场景

    本文用到的工具

    • 本机 Python 3.14.7
    • time.sleep(同步阻塞) vs asyncio.sleep(异步让出):对比
    京ICP备2026067502号-1京公网安备11010502063487号