C++ 无锁队列:从 CAS 到内存序
背景 只要做过高并发服务、游戏引擎或者低延迟组件,迟早会碰到一个问题:锁太重了。 典型场景包括: 生产者线程持续推消息 消费者线程高频拉取任务 临界区很短,但锁竞争很激烈 延迟指标对尾部抖动非常敏感 这时候很多人第一反应是“上无锁队列”。 方向没错,但无锁代码最危险的地方在于:看起来能跑,不代表一定正确。 尤其在 C++ 里,只会用 compare_exchange_weak 还不够,真正决定正确性的往往是内存序。 无锁不等于没有同步 先澄清一个常见误区: mutex 是同步 原子变量也是同步 无锁结构只是把同步方式从“阻塞锁”换成了“原子操作 + 内存可见性约束”。 也就是说,你不是不需要同步了,而是需要更精确地控制同步。 一个最简单的 SPSC 环形队列 先从单生产者、单消费者模型说起。这个模型更适合作为理解内存序的起点。 #include <atomic> #include <array> #include <cstddef> template <typename T, std::size_t N> class SpscQueue { public: bool push(const T& value) { const auto tail = tail_.load(std::memory_order_relaxed); const auto next = (tail + 1) % N; if (next == head_.load(std::memory_order_acquire)) { return false; } buffer_[tail] = value; tail_.store(next, std::memory_order_release); return true; } bool pop(T& value) { const auto head = head_.load(std::memory_order_relaxed); if (head == tail_.load(std::memory_order_acquire)) { return false; } value = buffer_[head]; head_.store((head + 1) % N, std::memory_order_release); return true; } private: std::array<T, N> buffer_{}; std::atomic<std::size_t> head_{0}; std::atomic<std::size_t> tail_{0}; }; 这个实现不复杂,但已经体现了两个关键点: ...