真正安全高性能的无锁队列需依赖原子操作、内存序与状态管理;SPSC场景推荐Michael-Scott环形缓冲区实现,MPMC则须用Hazard Pointer或EBR解决ABA与内存回收问题,优先选用moodycamel::ConcurrentQueue等成熟库。

实现一个真正安全、高性能的无锁队列(lock-free queue)在 C++ 中并不简单,它依赖于原子操作、内存序(memory order)和精细的状态管理。标准库没有提供 lock-free queue,std::queue 本身不是线程安全的,而 std::atomic 也不能直接用于复杂对象的无锁操作。下面讲清楚核心思路、关键陷阱和可落地的实现方式。
用 Michael-Scott 算法实现单生产者单消费者(SPSC)无锁队列
这是最实用、最容易正确实现的无锁队列场景。SPSC 避开了 ABA 问题和复杂的内存回收难题,适合高性能日志、网络收发缓冲等场景。
- 底层用环形缓冲区(circular buffer),两个原子整数分别记录 head(消费位置)和 tail(生产位置)
- 生产者只改 tail,消费者只改 head,无竞争;判断是否满/空时用取模比较,注意处理 wrap-around
- 关键:读写都用
memory_order_acquire/memory_order_release,避免指令重排破坏逻辑 - 示例片段:
tail.load(memory_order_acquire) - head.load(memory_order_acquire) 判断是否可入队
多生产者多消费者(MPMC)需解决 ABA 和内存回收问题
MPMC 是难点所在。当一个节点被出队后又被新节点复用,可能因指针重用导致 CAS 失败或崩溃(ABA 问题)。同时,谁来 delete 节点?多个线程可能同时访问同一节点。
- 常用方案:Hazard Pointer(危险指针)或 Epoch-based Reclamation(EBR)——不依赖引用计数,低开销且可预测
- 避免裸指针:用
std::atomic<node></node>管理 next 指针,所有 CAS 操作必须带memory_order_acq_rel - Michael-Scott 的 MPMC 变种需要为 head/tail 引入 dummy node,并对 tail 进行“两阶段”CAS(先占位再写值),防止丢失插入
- 不要自己手写 EBR;推荐使用成熟的无锁库如 moodycamel::ConcurrentQueue(工业级、经过大量压测)
别踩这些性能与正确性陷阱
很多“看起来像无锁”的实现,实际卡在锁、伪共享或内存序错误上,反而比加锁更慢、更难 debug。
标签: node facebook ai c++ nas 无锁 标准库
还木有评论哦,快来抢沙发吧~