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

Python中threading.Semaphore与asyncio.Semaphore的差异及使用疑问

threading.Semaphore 与 asyncio.Semaphore 的核心差异

  • 调度逻辑不同:threading.Semaphore 面向多OS线程场景设计,acquire() 拿不到信号量时会阻塞当前整个OS线程,由内核调度其他线程运行。asyncio.Semaphore 面向单线程协程场景设计,拿不到信号量时只会挂起当前协程,不会阻塞所在的OS线程,事件循环可继续调度其他就绪协程。
  • 底层实现不同:threading.Semaphore 依赖内核提供的同步原语(如Linux下的futex)实现,属于内核态同步机制。asyncio.Semaphore 完全是用户态实现,基于asyncio事件循环的协程挂起/唤醒逻辑运作,无内核态阻塞调用。
  • 适用范围不同:前者用于多OS线程间的资源并发限制,后者仅适用于同个事件循环内的协程间的资源并发限制。

异步函数中使用 threading.Semaphore 的风险

会直接导致异步事件循环卡死,完全失去异步性能优势。
原因很简单:threading.Semaphore.acquire() 是阻塞调用,只要拿不到信号量就会堵死当前整个OS线程,而asyncio的事件循环默认运行在单线程上,线程被阻塞后所有待调度的协程都会停止运行,哪怕是和这个信号量完全无关的异步任务也无法执行。
举个实际场景:如果你的信号量上限是5,同时有100个协程发起acquire请求,前5个拿到信号量之后,第6个协程调用acquire就会直接堵死整个事件循环,剩下的94个协程无论是否需要用到这个信号量,都无法被调度,直到阻塞的acquire调用返回为止。

官方“asyncio原语不是线程安全”说明的含义

官方原文如下:

asyncio primitives are not thread-safe, therefore they should not be used for OS thread synchronization (use threading for that)

这句话包含两层明确的要求:

  1. asyncio的所有同步原语(包括Semaphore、Lock、Event、Condition等)内部没有做任何多线程并发访问的防护,如果你在多个OS线程中同时操作同一个asyncio原语实例,会触发竞态条件,出现计数异常、漏唤醒、死锁等不可预期的逻辑错误。
  2. asyncio同步原语的设计目标就不是解决OS线程之间的同步问题,它的作用域仅限于同一个OS线程下、归属同一个事件循环的协程之间的同步。如果你需要实现多个OS线程之间的同步,必须使用threading模块下的对应同步原语,不要用asyncio的。

常见的错误用法示例:在A线程的事件循环中创建asyncio.Semaphore实例,然后传给B线程中的逻辑调用acquire/release,这种写法几乎一定会出现难以排查的bug。

内容的提问来源于stack exchange,提问作者user8942442

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 11:45:04