信号量表示“当前有多少份可用许可”。acquire() 在许可大于零时原子地取走一个,否则等待;release() 归还一个或多个许可并唤醒等待者。它非常适合限制有限资源,例如数据库连接、GPU 推理槽位、固定缓冲区、并发下载数,也可表达生产者/消费者中“已有数据项”的数量。它与 mutex 都会让线程等待,但关注的对象不同。
mutex、condition variable 与 semaphore 的分工
mutex 保护某段临界区或共享状态,通常一次只允许一个线程进入;condition variable 用“状态谓词 + 唤醒”协调等待;计数信号量直接表示可并发使用的资源数量,允许最多 N 个线程同时通过。若你真正需要保护的是一个复杂状态不变量,信号量不能替代 mutex;若资源是固定数量的独立槽位,信号量比手写计数器加条件变量更直接。
固定缓冲池的典型用法
1 | std::counting_semaphore<kSlots> free_slots{kSlots}; |
信号量只保证许可计数,不管理 take_free_slot() 返回哪个对象,也不保证队列本身线程安全。实际池仍需要一个正确的空闲列表/队列、对象生命周期协议和异常路径:一旦填充或发布失败,也要归还 slot 和许可。
容量、取消与公平性
标准信号量不承诺严格公平,等待者被唤醒的顺序不应作为业务正确性前提。C++20 提供 try_acquire_for/try_acquire_until,可为超时设计退出路径;若任务可取消,还要在 acquire 前后检查 stop token 并确保不会泄漏许可。长期占住许可的任务等同于缩小系统容量,应该被超时、监控和资源审计发现。
对于实时系统,等待信号量意味着 deadline 依赖另一个任务释放资源。要么将资源数和执行预算设计得足够明确,要么改为预分配、无等待的控制路径。信号量让有限资源的数量显式化,但不会自动消除排队延迟。