不能直接用std::queue做lock-free队列,因为它底层非原子、不支持整体原子化,且含多成员需同步;必须基于std::atomic指针+单链表节点手动实现,并解决ABA、内存回收等核心问题。

为什么不能直接用 std::queue 做 lock-free 队列
因为 std::queue 底层依赖 std::deque 或 std::list,所有插入/删除操作都不是原子的,且内部有多个成员变量(如头尾指针、size 等)需要同步。即使你用 std::atomic 包裹整个对象,C++ 也不允许对非平凡类型做整体原子操作——编译会直接报错:error: use of deleted function 'std::atomic<:queue>>::atomic()'</:queue>。
真正可行的起点是:从原子指针 + 单链表节点出发,自己管理内存生命周期。
用 std::atomic + 单链表实现基础无锁入队/出队
核心结构是一个带 next 指针的节点,头尾用 std::atomic<node></node> 维护。注意:仅靠 compare_exchange_weak 不足以保证线程安全,必须处理 ABA 问题(尤其在出队时)。
- 入队(
push)只需 CAS 更新尾指针,但要先确保尾节点的next为空,再挂上新节点,最后移动尾指针——这至少需要两次 CAS - 出队(
pop)必须同时读取头节点和它的next,再用一次双字 CAS(std::atomic<:pair node>></:pair>不行,得用std::atomic<uint64_t></uint64_t>手动打包指针+版本号) - 不回收内存?那会内存泄漏;直接
delete?可能有其他线程正读这个节点。必须用 Hazard Pointer 或 RCU,但最简方案是用std::shared_ptr——虽然它本身不是 lock-free,但能避免悬空指针
示例关键片段(无内存回收):
立即学习“C++免费学习笔记(深入)”;
struct Node {
T data;
std::atomic<Node*> next{nullptr};
};
<p>std::atomic<Node<em>> head{nullptr};
std::atomic<Node</em>> tail{nullptr};</p><p>void push(T val) {
Node<em> n = new Node{val};
Node</em> t;
do {
t = tail.load();
Node* t_next = t->next.load();
if (t == tail.load()) {
if (!t_next) { // t 是真正的尾
if (t->next.compare_exchange_weak(t_next, n))
break;
} else {
tail.compare_exchange_weak(t, t_next); // 推进 tail
}
}
} while (true);
tail.compare_exchange_weak(t, n);
}为什么 boost::lockfree::queue 更靠谱但仍有约束
它底层用的是 Michael-Scott 算法,已处理 ABA 和内存重用问题,但要求元素类型满足 is_trivially_destructible,且默认固定容量(循环数组实现)。如果你传入 std::string 或含虚函数的对象,编译失败信息会指向 static_assert 失败,而不是运行时报错。
- 动态扩容?不支持。得初始化时指定最大大小,比如
boost::lockfree::queue<int>(1024)</int> - 想用堆内存自动管理?不行。它只支持 trivial 类型,否则析构逻辑无法在无锁上下文中安全执行
- 性能敏感场景下,它的 CAS 热点集中在 tail 指针,高并发 push 可能比 mutex 版本还慢(因大量失败重试)
实际项目中更推荐的折中方案
真需要吞吐量又不想掉坑,与其手写 lock-free 队列,不如用带细粒度锁的「多生产者单消费者」(MPSC)队列,比如 moodycamel::ConcurrentQueue。它用分离的生产者环形缓冲区 + 全局消费索引,大部分 push 是无锁的,pop 也只锁一次。
- 支持任意可拷贝类型,包括
std::vector、std::function - 没有固定容量限制(可配置增长策略),也无需手动管理内存
- 比纯 lock-free 实现更容易调试——比如
enqueue失败会返回false,而不是静默丢任务
真正难的从来不是“怎么写 CAS”,而是“怎么让所有线程在任意中断点下都看到一致的中间状态”。哪怕只漏掉一个内存序(memory_order_relaxed 用错位置),行为就不可预测。


















