【C/C++】無鎖隊列實現與內存回收機制:Hazard Pointer 深度解析

無鎖隊列實現與內存回收機制:Hazard Pointer 深度解析

在并發系統中,為了提升性能和避免鎖競爭,我們常常追求 lock-free 數據結構。但當你實現完一個無鎖隊列后,會發現一個嚴重問題:

內存什么時候釋放?怎樣避免訪問已經被回收的節點?

Hazard Pointer(危險指針)機制,就是為了解決這一痛點。

本篇將帶你深入理解:

  • 什么是無鎖隊列(MS Queue)
  • 為什么需要 Hazard Pointer
  • 如何用 C++ 實現完整的無鎖隊列 + 安全回收機制
  • 避免 ABA 問題的技巧

一、無鎖隊列背景:MS 隊列簡介

Michael-Scott 隊列(MS Queue)是經典的無鎖并發隊列,具備以下特性:

  • 使用 std::atomic 實現
  • 支持多生產者、多消費者
  • 基于 CAS(Compare-And-Swap)進行并發控制

隊列結構:

   head --> [dummy] --> [node1] --> [node2] --> nullptr^tail
  • 使用 dummy 節點初始化隊列,避免空指針特判。
  • enqueue 修改 tail,dequeue 修改 head。

二、C++ 無鎖隊列實現(簡化版)

template <typename T>
struct Node {std::atomic<Node*> next;T value;Node(const T& val) : next(nullptr), value(val) {}
};template <typename T>
class LockFreeQueue {
private:std::atomic<Node<T>*> head;std::atomic<Node<T>*> tail;public:LockFreeQueue() {Node<T>* dummy = new Node<T>(T{});head.store(dummy);tail.store(dummy);}void enqueue(const T& val) {Node<T>* new_node = new Node<T>(val);while (true) {Node<T>* last = tail.load();Node<T>* next = last->next.load();if (last == tail.load()) {if (!next) {if (last->next.compare_exchange_weak(next, new_node)) {tail.compare_exchange_weak(last, new_node);return;}} else {tail.compare_exchange_weak(last, next);}}}}bool dequeue(T& result) {while (true) {Node<T>* first = head.load();Node<T>* last = tail.load();Node<T>* next = first->next.load();if (first == head.load()) {if (first == last) {if (!next) return false; // emptytail.compare_exchange_weak(last, next);} else {result = next->value;if (head.compare_exchange_weak(first, next)) {// ?問題:何時 delete first ?return true;}}}}}
};

問題:節點釋放時機?

在并發環境下 dequeue 之后,其他線程可能仍在訪問那個被刪除的節點,不能立即 delete。


三、為什么需要 Hazard Pointer?

常見方法(失敗):

  • 延遲 delete? 不知道什么時候沒人訪問。
  • GC? C++ 沒有自動垃圾回收。
  • 引用計數? 有性能開銷,難以 lock-free。
  • 線程局部鏈表回收? 難以同步他人狀態。

Hazard Pointer 的核心思想:

每個線程維護一個“我正在訪問的指針”集合,回收前先檢查是否有人正在訪問。


四、Hazard Pointer 機制原理

數據結構:

// 線程局部
thread_local Node* my_hazard_ptr;// 全局列表
std::vector<std::atomic<Node*>> hazard_pointers;

工作流程:

  1. 訪問某節點前,將其地址寫入 hazard pointer。

  2. 刪除時,把要刪除的節點放入“回收候選集合”。

  3. 定期掃描所有線程的 hazard pointer:

    • 如果節點不在任何 hazard 中,則可以安全 delete。

回收偽碼:

void retire_node(Node* node) {retired_list.push_back(node);if (retired_list.size() >= threshold) {scan_and_reclaim();}
}void scan_and_reclaim() {std::unordered_set<Node*> hp_set;for (auto& hp : hazard_pointers) {Node* p = hp.load();if (p) hp_set.insert(p);}auto it = retired_list.begin();while (it != retired_list.end()) {if (hp_set.count(*it) == 0) {delete *it;it = retired_list.erase(it);} else {++it;}}
}

五、完整工程結構(建議)

lockfree_queue/
├── include/
│   ├── lockfree_queue.h         // MS隊列
│   └── hazard_pointer.h         // Hazard機制封裝
├── src/
│   ├── hazard_pointer.cpp
│   └── main.cpp
├── test/
│   └── test_queue.cpp           // GTest 測試用例
├── CMakeLists.txt

示例:Hazard 指針封裝類接口

class HazardPointerManager {
public:static void* get_slot(); // 獲取當前線程 hazard pointer 槽static void retire(void* ptr);static void reclaim(); // 回收所有未被引用的節點
};

六、ABA 問題與 Hazard Pointer 的結合防護

雖然 Hazard Pointer 可以避免野指針訪問,但不能防止 ABA 問題。

因此,通常會在無鎖隊列中配合使用 Tagged Pointer(帶版本號的指針)

struct TaggedPtr {Node* ptr;uint64_t tag;
};
std::atomic<TaggedPtr> head;

通過不斷增加 tag 可以有效防止 ABA。


七、總結與建議

問題是否解決說明
并發入隊?CAS 更新尾指針
并發出隊?CAS 更新頭指針
節點回收?Hazard Pointer 掃描后安全 delete
ABA 問題?(需 TaggedPtr)防止假象“未變化”

八、參考資料

  • Michael & Scott: “Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms”
  • Herb Sutter: “Lock-Free Programming (Hazard Pointers)”
  • C++ Concurrency in Action 第二版
  • Facebook Folly Hazard Pointer 實現

下期預告

《設計一個可擴展的 Lock-Free 環:支持動態擴容、線程綁定與優先級任務》

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/88605.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/88605.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/88605.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Scrapy進階封裝(第三階段:多管道封裝,多文件存儲)

1.yield返回數據的原理? 為什么要用yield返回數據給管道&#xff1f; 遍歷這個函數的返回值的時候&#xff0c;挨個把數據讀到內存&#xff0c;不會造成內存的瞬間占用過高&#xff0c;Python3中的range和python2中的xrange同理。scrapy是異步爬取&#xff0c;所以通過yield…

證照大師 MAX 4.0安裝與基礎功能體驗(附流程演示)

軟件介紹 證照大師 MAX 4.0是一款功能強大的證件照制作軟件&#xff0c;專為滿足用戶不同場景下的證件照需求而設計。它整合了專業的照片處理技術和智能化的操作系統&#xff0c;提供了自動摳圖、尺寸調整、美顏處理、批量處理以及格式轉換等多種功能。該軟件用戶界面簡潔明快…

RK3568-適配mipi屏幕觸摸和顯示

1.1 適配mipi屏幕觸摸 gt9xx_lvds: gt9xx-lvds5d {compatible "goodix,gt9xx";reg <0x5d>;pinctrl-names "default";pinctrl-0 <&touch_gpio>;touch-gpio <&gpio1 RK_PA4 IRQ_TYPE_LEVEL_LOW>;reset-gpio <&gpio1…

ICME 2025音頻編碼器能力挑戰賽Workshop即將舉辦!

IEEE International Conference on Multimedia and Expo 2025&#xff08;ICME 2025&#xff09; 將于 6月30日至7月4日在法國南特舉行。作為全球多媒體領域的頂級會議之一&#xff0c;ICME 2025 匯聚全球頂尖學者與產業專家&#xff0c;聚焦人工智能驅動的多媒體技術&#xff…

物奇微WQ5007A上手指南

一、獲取SDK 需要與物奇微電子股份有限公司簽訂NDA協議才會提供SDK。 二、搭建開發環境 SDK里包含了編譯工具、開發文檔、源碼。在windows系統下搭建開發環境&#xff1a; 1、安裝交叉編譯工具 將\wuqi_sdk\tools\riscv64-unknown-elf-gcc-10.2.0-windows.zip文件解壓到任…

[論文閱讀] 人工智能 + 軟件工程 | LLM在單元測試中的應用:系統性綜述與未來展望

LLM在單元測試中的應用&#xff1a;系統性綜述與未來展望 論文信息 arXiv:2506.15227 Large Language Models for Unit Testing: A Systematic Literature Review Quanjun Zhang, Chunrong Fang, Siqi Gu, Ye Shang, Zhenyu Chen, Liang Xiao Subjects: Software Engineering …

數據重疊對CLIP零樣本能力影響CLIP論文圖17筆記

這兩張圖表&#xff08;圖17左、右圖&#xff09;是CLIP論文中驗證“數據重疊是否影響CLIP零樣本能力”的關鍵證據&#xff0c;核心是通過**“數據重疊分析”排除CLIP“作弊”嫌疑**&#xff08;即CLIP的高零樣本準確率是否因為“見過測試集圖像”&#xff09;。下面用“先看懂…

996引擎-假人系統

996引擎-假人系統 lua 假人問題添加假人名字列表打開M2設置假人參考資料 lua 假人問題 添加假人名字列表 假人名字列表 Mir200\Envir\DummyNameList.txt 打開M2設置假人 【選項】>【假人設置】 參考資料 假人系統

Rk3568驅動開發_Key驅動_13

設備樹配置 key{compatible "alientek,key";pinctrl-0 <&key_gpio>;pinctrl-names "alientek,key";key-gpio <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>;status "okay";};配置信息方便后面直接引用&#xff1a; // Narnat 2025…

參展回顧 | AI應用創新場景:數據分析助手ChatBI、璞公英教學平臺亮相2025四川國際職教大會暨產教融合博覽會

2025年6月11日-13日&#xff0c;以“數字賦能產教融合&#xff0c;創新驅動技能未來”為主題的2025四川國際職業教育大會暨產教融合博覽會在成都盛大開幕。璞華聯合百度共同參展&#xff0c;并攜旗下創新產品ChatBI數據分析助手、璞公英教學平臺重磅亮相&#xff0c;憑借前沿的…

動態規劃之01背包問題

動態規劃算法 動態規劃算法介紹 動態規劃(Dynamic Programming)算法的核心思想是&#xff1a;將大問題劃分為小問題進行解決&#xff0c;從而一步步獲取最優解的處理算法動態規劃算法與分治法類似&#xff0c;其基本思想也是將待解決問題分解成若干個子問題&#xff0c;先求解…

人大金倉新建用戶,并且賦值查詢權限

-- 1. 創建用戶 visitor&#xff0c;并且設置密碼 CREATE USER visitor WITH PASSWORD 1234qwer; -- 2. 授予該用戶連接到數據庫 "yonbip_db" 的權限 GRANT CONNECT ON DATABASE yonbip_db TO visitor; -- 3. 假設你要讓 visitor 查詢的模式是 public&#xff08;或…

學習筆記丨信號處理新趨勢:量子計算將如何顛覆傳統DSP?

在算力需求爆炸式增長的今天&#xff0c;傳統數字信號處理&#xff08;DSP&#xff09;芯片正面臨物理極限的嚴峻挑戰。當經典計算機架構在摩爾定律的黃昏中掙扎時&#xff0c;量子計算正以顛覆性姿態崛起&#xff0c;準備重新定義信號處理的未來圖景。 目錄 傳統DSP的瓶頸&am…

react day.js使用及經典場景

簡介 Day.js 是一個輕量級的 JavaScript 日期庫&#xff0c;它提供了簡單易用的 API 來處理日期和時間。以及更加輕量級&#xff0c;并且具有更快的性能。 安裝 npm install dayjs 使用 import dayjs from "dayjs";dayjs().format("YYYY-MM-DD HH:mm:ss&qu…

【機器學習深度學習】線性回歸

目錄 一、定義 二、舉例說明 三、 數學形式 四、 訓練過程&#xff08;機器怎么學會這條線&#xff1f;&#xff09; 五、在 PyTorch 中怎么實現線性回歸&#xff1f; 六、如果你學懂了線性回歸&#xff0c;你也能理解這些 七、綜合應用&#xff1a;線性回歸示例 7.1 執…

如何在 Manjaro Linux 上安裝 .NET Core

.NET 是一個開源的開發框架平臺,可在所有流行的操作系統(如 Windows、Linux 和 macOS)上免費使用和安裝。它是跨平臺的,是主要由微軟員工在 .NET 基金會下開發的專有 .NET Framework 的繼承者。.NET 是一個統一的平臺,用于開發各種操作系統上的軟件,如 Web、移動、桌面應…

Mysql解惑(一)

使用 or 可能不走索引 使用 union替代 使用in&#xff0c;可能不走索引 如果優化&#xff1a; 臨時表強制索引exists代替

基于機器學習的側信道分析(MLSCA)Python實現(帶測試)

一、MLSCA原理介紹 基于機器學習的側信道分析(MLSCA)是一種結合傳統側信道分析技術與現代機器學習算法的密碼分析方法。該方法通過分析密碼設備運行時的物理泄漏信息(如功耗、電磁輻射等)&#xff0c;利用機器學習模型建立泄漏數據與密鑰信息之間的關聯模型&#xff0c;從而實…

【LLM】位置編碼

【LLM】位置編碼 1 絕對位置嵌入為什么用 1000 0 2 t d 10000^{\frac{2t}{d}} 10000d2t?? 2 相對位置嵌入2.1 Shaw等人的方法&#xff08;2018&#xff09;2.2 Dai等人的方法&#xff08;2019&#xff09;2.3 Raffel 等人的方法&#xff08;2020&#xff09;2.4 He 等人的方法…

Java 根據分組key構建合并數據集

文章目錄 前言背景總結 前言 請各大網友尊重本人原創知識分享&#xff0c;謹記本人博客&#xff1a;南國以南i、 提示&#xff1a;以下是本篇文章正文內容&#xff0c;下面案例可供參考 背景 Java 需要返回一組數據供前端展示&#xff0c;獲取到的數據格式如下&#xff1a; …