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
相关推荐
蒸蒸yyyyzwd6 小时前
秋招学习笔记 day46
c++·八股
码匠许师傅9 小时前
【C++三方组件】cpp-httplib:一个头文件起 HTTP 服务
开发语言·c++·http
无名猿10 小时前
new/delete 与 malloc/free:为什么绝对不能混用
c++·内存管理·现代c++·踩坑记录
2601_9622186112 小时前
C++中decltype关键字的实现
开发语言·c++
繁华的地方不一定留下你的脚印13 小时前
C++ std::variant 与 std::visit:安全保存多种类型,写清每个处理分支
开发语言·c++
程序员老陆13 小时前
WIN32_LEAN_AND_MEAN:Windows 头文件里的“精简编译开关”
c++·windows
hold?fish:palm14 小时前
45 二叉树的右视图
开发语言·c++·算法
ShineWinsu14 小时前
对于Redis:Redis的认识以及分布式系统的解析
linux·数据库·c++·redis·缓存·消息队列·分布式架构
布莱克60514 小时前
在C++中,构造函数能不能是虚函数?析构函数为什么要虚?
开发语言·c++·构造函数·析构函数·虚函数
旖旎夜光15 小时前
LeetCode 137 : 只出现一次的数字 II(位运算) —— 题解
c++·笔记·学习·算法·leetcode·力控