Epoch / QSBR 内存回收解决ABA问题

✅ 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
相关推荐
迷途之人不知返3 小时前
优先级队列:priority_queue
数据结构·c++
曦夜日长3 小时前
C++ STL容器string(一):string的变量细节、默认函数的认识以及常用接口的使用
java·开发语言·c++
代码中介商3 小时前
C++ STL 标准模板库完全指南:从容器到迭代器
开发语言·c++·stl
winner88813 小时前
C++ 构造函数、析构函数、虚函数、虚析构
开发语言·c++
想唱rap3 小时前
应用层协议与序列化
linux·运维·服务器·网络·数据结构·c++·算法
许长安3 小时前
protobuf 使用详解
c++·经验分享·笔记·中间件
Soley3 小时前
用 Boost.Log 封装一个更顺手的 C++17 日志库:GoodLog
c++
HAPPY酷4 小时前
从Public到Private:UE5 C++类创建路径差异全解析
java·c++·ue5
无敌昊哥战神4 小时前
【LeetCode 37】解数独 (Sudoku Solver) —— 回溯法详解 (Python/C/C++)
c语言·c++·python·算法·leetcode
AI进化营-智能译站4 小时前
ROS2 C++开发系列08-传感器数据缓存与指令解析方式之数组、向量与字符串实战
开发语言·c++·缓存·ai