Hazard Pointers(危险指针) ✅ 作用:保护正在访问的指针,防止它被其他线程释放。
每个线程维护一组“hazard slots”,用于声明当前正在使用的节点。
在释放节点前,必须检查是否有线程将其标记为 hazard。如果有,就延迟释放。
📌 优点:精细控制:每个指针都可以独立保护。
快速释放:一旦没有线程标记该节点,就可以立即释放。
⚠️ 缺点:每个线程需要维护额外的指针集合。
实现复杂度较高,尤其在高并发场景下。
Epoch-Based Reclamation(基于世代的回收) ✅ 作用:将所有线程的操作划分到一个“epoch”(时间段)中。
每个线程在进入临界区时记录当前 epoch,退出时清除。
被删除的节点会挂到当前 epoch 的垃圾列表中,只有当所有线程都离开该 epoch,才安全释放这些节点。
📌 优点:实现简单,性能好,适合高吞吐量场景。
不需要每个指针都单独保护。
⚠️ 缺点:回收延迟:必须等待所有线程退出当前 epoch。
对长时间运行的线程敏感,可能阻止垃圾回收。
🧠 为什么需要它们?
在 lock-free 数据结构中,节点可能被多个线程同时访问或修改。没有互斥锁保护时,直接释放内存可能导致:
悬挂指针:其他线程还在访问被释放的节点。
ABA 问题:节点被释放后地址复用,导致逻辑错误。
ebr.cpp view raw 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <atomic> #include <iostream> #include <mutex> #include <thread> #include <unordered_map> #include <vector> constexpr int MAX_THREADS = 4 ;std::atomic<int > global_epoch{0 }; std::atomic<bool > active[MAX_THREADS]; std::atomic<int > local_epoch[MAX_THREADS]; std::mutex retire_mutex; std::unordered_map<int , std::vector<int *>> retire_list[3 ]; void enter_critical (int tid) { active[tid].store (true , std::memory_order_relaxed); local_epoch[tid].store (global_epoch.load (std::memory_order_relaxed), std::memory_order_relaxed); } void exit_critical (int tid) { active[tid].store (false , std::memory_order_relaxed); } void retire_node (int tid, int * node) { std::lock_guard<std::mutex> lock (retire_mutex) ; int epoch = global_epoch.load (std::memory_order_relaxed); retire_list[epoch][tid].push_back (node); } void try_advance_epoch () { int current = global_epoch.load (std::memory_order_relaxed); for (int i = 0 ; i < MAX_THREADS; ++i) { if (active[i].load (std::memory_order_relaxed) && local_epoch[i].load (std::memory_order_relaxed) != current) { return ; } } int next_epoch = (current + 1 ) % 3 ; global_epoch.store (next_epoch, std::memory_order_relaxed); int reclaim_epoch = (next_epoch + 1 ) % 3 ; std::lock_guard<std::mutex> lock (retire_mutex) ; for (auto & [tid, nodes] : retire_list[reclaim_epoch]) { for (auto node : nodes) { delete node; } nodes.clear (); } }