✅ MPMC 无锁队列(Michael--Scott)
✅ Epoch / QSBR 内存回收
✅ 解决 ABA
✅ 多生产者 / 多消费者测试
✅ C++17,一条 g++ 命令即可跑
我刻意压缩到"教学 + 工程平衡点":
不是最极限性能,但逻辑完整、不会 UAF、不会 ABA,非常适合你现在的理解阶段。
cpp
#include <atomic>
#include <thread>
#include <vector>
#include <iostream>
#include <cassert>
#include <chrono>
// ============================================================
// 1. Epoch / QSBR 实现
// ============================================================
class EpochManager {
public:
static constexpr size_t MAX_THREADS = 64;
struct ThreadEpoch {
std::atomic<uint64_t> epoch;
std::atomic<bool> active;
};
static EpochManager& instance() {
static EpochManager inst;
return inst;
}
size_t register_thread() {
for (size_t i = 0; i < MAX_THREADS; ++i) {
bool expected = false;
if (threads_[i].active.compare_exchange_strong(expected, true)) {
threads_[i].epoch.store(global_epoch_.load(std::memory_order_relaxed),
std::memory_order_relaxed);
return i;
}
}
throw std::runtime_error("Too many threads");
}
void unregister_thread(size_t tid) {
threads_[tid].active.store(false, std::memory_order_release);
}
void enter(size_t tid) {
threads_[tid].epoch.store(global_epoch_.load(std::memory_order_acquire),
std::memory_order_release);
}
void exit(size_t /*tid*/) {
// QSBR: no-op
}
void advance_epoch() {
global_epoch_.fetch_add(1, std::memory_order_acq_rel);
}
bool can_reclaim(uint64_t retire_epoch) {
for (auto& t : threads_) {
if (t.active.load(std::memory_order_acquire) &&
t.epoch.load(std::memory_order_acquire) <= retire_epoch) {
return false;
}
}
return true;
}
uint64_t current_epoch() const {
return global_epoch_.load(std::memory_order_acquire);
}
private:
EpochManager() {
for (auto& t : threads_) {
t.active.store(false);
t.epoch.store(0);
}
}
std::atomic<uint64_t> global_epoch_{1};
ThreadEpoch threads_[MAX_THREADS];
};
// ============================================================
// 2. Epoch Reclaimer
// ============================================================
template<typename T>
class EpochReclaimer {
public:
void retire(T* ptr) {
retired_.push_back({ptr, EpochManager::instance().current_epoch()});
if (retired_.size() >= 32)
try_reclaim();
}
void try_reclaim() {
auto& mgr = EpochManager::instance();
auto it = retired_.begin();
while (it != retired_.end()) {
if (mgr.can_reclaim(it->epoch)) {
delete it->ptr;
it = retired_.erase(it);
} else {
++it;
}
}
}
private:
struct Retired {
T* ptr;
uint64_t epoch;
};
std::vector<Retired> retired_;
};
// ============================================================
// 3. MPMC Lock-Free Queue (Michael--Scott + Epoch)
// ============================================================
template<typename T>
class MPMCQueue {
private:
struct Node {
T data;
std::atomic<Node*> next;
Node() : next(nullptr) {}
Node(const T& v) : data(v), next(nullptr) {}
};
std::atomic<Node*> head_;
std::atomic<Node*> tail_;
EpochReclaimer<Node> reclaimer_;
public:
MPMCQueue() {
Node* dummy = new Node();
head_.store(dummy, std::memory_order_relaxed);
tail_.store(dummy, std::memory_order_relaxed);
}
~MPMCQueue() {
T tmp;
while (dequeue(tmp)) {}
delete head_.load();
}
void enqueue(const T& value, size_t tid) {
EpochManager::instance().enter(tid);
Node* node = new Node(value);
while (true) {
Node* last = tail_.load(std::memory_order_acquire);
Node* next = last->next.load(std::memory_order_acquire);
if (last == tail_.load(std::memory_order_acquire)) {
if (next == nullptr) {
if (last->next.compare_exchange_weak(
next, node,
std::memory_order_release,
std::memory_order_relaxed)) {
tail_.compare_exchange_weak(
last, node,
std::memory_order_release,
std::memory_order_relaxed);
break;
}
} else {
tail_.compare_exchange_weak(
last, next,
std::memory_order_release,
std::memory_order_relaxed);
}
}
}
EpochManager::instance().exit(tid);
}
bool dequeue(T& result, size_t tid) {
EpochManager::instance().enter(tid);
while (true) {
Node* first = head_.load(std::memory_order_acquire);
Node* last = tail_.load(std::memory_order_acquire);
Node* next = first->next.load(std::memory_order_acquire);
if (first == head_.load(std::memory_order_acquire)) {
if (first == last) {
if (next == nullptr) {
EpochManager::instance().exit(tid);
return false;
}
tail_.compare_exchange_weak(
last, next,
std::memory_order_release,
std::memory_order_relaxed);
} else {
result = next->data;
if (head_.compare_exchange_weak(
first, next,
std::memory_order_release,
std::memory_order_relaxed)) {
reclaimer_.retire(first);
EpochManager::instance().advance_epoch();
EpochManager::instance().exit(tid);
return true;
}
}
}
}
}
};
// ============================================================
// 4. 测试程序(MPMC)
// ============================================================
int main() {
constexpr int PRODUCERS = 4;
constexpr int CONSUMERS = 4;
constexpr int ITEMS_PER_PRODUCER = 100000;
MPMCQueue<int> queue;
std::atomic<int> produced{0};
std::atomic<int> consumed{0};
std::vector<std::thread> threads;
// Producers
for (int i = 0; i < PRODUCERS; ++i) {
threads.emplace_back([&queue, &produced]() {
size_t tid = EpochManager::instance().register_thread();
for (int n = 0; n < ITEMS_PER_PRODUCER; ++n) {
queue.enqueue(n, tid);
produced.fetch_add(1, std::memory_order_relaxed);
}
EpochManager::instance().unregister_thread(tid);
});
}
// Consumers
for (int i = 0; i < CONSUMERS; ++i) {
threads.emplace_back([&queue, &consumed, &produced]() {
size_t tid = EpochManager::instance().register_thread();
int value;
while (true) {
if (queue.dequeue(value, tid)) {
consumed.fetch_add(1, std::memory_order_relaxed);
} else {
if (consumed.load() >= PRODUCERS * ITEMS_PER_PRODUCER)
break;
std::this_thread::yield();
}
}
EpochManager::instance().unregister_thread(tid);
});
}
for (auto& t : threads)
t.join();
std::cout << "Produced: " << produced.load() << "\n";
std::cout << "Consumed: " << consumed.load() << "\n";
std::cout << "Done.\n";
}
bash
g++ -std=c++17 -O2 -pthread epoch_mpmc.cpp -o epoch_mpmc
./epoch_mpmc