无锁队列实现
逐步学习单生产者单消费者无锁队列的设计
无锁队列实现 是 CoddyKit 上的免费 C++ Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C++ Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C++ Academy 课程共包含 4 节课。
为什么需要无锁队列?
在高竞争情况下,带互斥锁的队列可能成为瓶颈。无锁队列允许生产者和消费者并发取得进展。
SPSC 与 MPMC
两种类型:
- SPSC — 单生产者、单消费者(最简单、最快)
- MPMC — 多生产者、多消费者(最通用)
当您同时控制两端时,SPSC 是自然的选择。
SPSC 环形缓冲区示意图
一个包含两个索引的环形缓冲区:head(消费者)和 tail(生产者)。每一方只更新自己的索引。
template <typename T, size_t N>
class SpscQueue {
T buffer_[N];
std::atomic<size_t> head_{0};
std::atomic<size_t> tail_{0};
public:
bool push(const T& v);
bool pop(T& v);
};SPSC 推入
生产者检查空闲槽位,写入数据,然后通过更新 tail 发布数据。
bool push(const T& v) {
const size_t t = tail_.load(std::memory_order_relaxed);
const size_t next = (t + 1) % N;
if (next == head_.load(std::memory_order_acquire))
return false; // full
buffer_[t] = v;
tail_.store(next, std::memory_order_release);
return true;
}SPSC 弹出
消费者检查是否有数据,读取数据,然后通过更新 head 发布操作完成。
bool pop(T& v) {
const size_t h = head_.load(std::memory_order_relaxed);
if (h == tail_.load(std::memory_order_acquire))
return false; // empty
v = buffer_[h];
head_.store((h + 1) % N, std::memory_order_release);
return true;
}内存顺序配对
tail 上的 release-store 会与消费者对 tail 执行的 acquire-load 同步(反之亦然)。如果顺序不正确,数据写入可能会被重排到索引更新之后。
缓存行填充
为避免伪共享,请将 head_ 和 tail_ 放在不同的缓存行上(通常相隔 64 字节)。请使用 alignas。
alignas(64) std::atomic<size_t> head_{0};
alignas(64) std::atomic<size_t> tail_{0};MPMC:难得多
多个生产者或消费者需要额外的协调,通常要在共享索引上使用 CAS 循环。现有许多设计(Vyukov 队列、MS 队列、基于危险指针的队列)。
Boost.Lockfree
达到生产质量的无锁队列很难实现。请使用 Boost.Lockfree 或 Folly 的 ProducerConsumerQueue,而不要自行实现。
权衡
无锁队列:
- 在竞争情况下吞吐量更高
- 延迟有界(无需等待锁)
- 编写和调试难得多
- 内存顺序错误不会显现出来,而且难以追踪
测试无锁代码
使用 ThreadSanitizer(-fsanitize=thread)捕获数据竞争。使用插入随机 Sleep 的压力测试来暴露顺序错误。
互斥锁已经足够时
大多数应用不需要无锁队列。请先进行测量——实现良好的互斥锁保护队列通常就能满足性能要求,尤其是在批量处理时。
快速检查
什么是伪共享?为什么要对 head_ 和 tail_ 进行填充?
小结
无锁 SPSC 队列使用环形缓冲区,其中 tail 由生产者拥有,head 由消费者拥有。请使用 acquire/release 顺序,并填充索引,使它们位于不同的缓存行中。对于 MPMC,优先使用经过测试的库。
常见问题解答
「无锁队列实现」课时是免费的吗?
是的 — 「无锁队列实现」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C++ Academy 课程的其余内容,请升级到 CoddyKit PRO。 C++ Academy 课程共包含 4 节课。
「无锁队列实现」这节课中我会学到什么?
逐步学习单生产者单消费者无锁队列的设计 你通过在浏览器中直接运行的动手代码来练习 C++ Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C++ Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C++ Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「无锁队列实现」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C++ Academy 课中编写并运行代码吗?
能。每节 C++ Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。