C++無鎖數據結構:CAS(Compare-and-Swap)

在高并發場景下,傳統鎖機制帶來的線程阻塞和上下文切換開銷成為性能瓶頸。無鎖數據結構通過原子操作實現線程安全,避免了鎖的使用,成為高性能系統的關鍵技術。本文將深入探討C++中基于CAS(Compare-and-Swap)的無鎖數據結構實現原理、應用場景及實戰技巧。

一、CAS原理解析

1.1 什么是CAS?

CAS是一種原子操作,包含三個參數:內存地址(V)、舊值(A)和新值(B)。操作邏輯如下:

  • 如果內存地址V中的值等于舊值A,則將該位置的值更新為新值B
  • 否則返回當前內存中的實際值

CAS操作通常由CPU硬件直接支持,是無鎖編程的核心原語。

1.2 C++中的CAS實現

C++11在<atomic>頭文件中提供了原子操作支持,其中compare_exchange_weakcompare_exchange_strong對應CAS操作:

#include <atomic>template<typename T>
bool atomic_compare_and_swap(std::atomic<T>& atom, T& expected, T desired) {return atom.compare_exchange_weak(expected, desired);
}
  • compare_exchange_weak:可能會虛假失敗(即使值相等也可能返回false),但性能更高
  • compare_exchange_strong:保證不會虛假失敗,但可能需要更多次重試

1.3 CAS的ABA問題

CAS操作存在ABA問題:當值從A變為B再變回A時,CAS會誤認為值沒有變化。解決方法通常是使用帶版本號的原子變量:

template<typename T>
struct VersionedValue {T value;uint64_t version;bool operator==(const VersionedValue& other) const {return value == other.value && version == other.version;}
};std::atomic<VersionedValue<int>> atom;// 更新操作
void update(int new_value) {VersionedValue<int> expected = atom.load();VersionedValue<int> desired;do {desired.value = new_value;desired.version = expected.version + 1;} while (!atom.compare_exchange_weak(expected, desired));
}

二、基于CAS的無鎖棧實現

2.1 單鏈表節點結構

template<typename T>
struct Node {T data;Node* next;Node(const T& value) : data(value), next(nullptr) {}
};

2.2 無鎖棧核心實現

template<typename T>
class LockFreeStack {
private:std::atomic<Node<T>*> head;public:LockFreeStack() : head(nullptr) {}// 入棧操作void push(const T& value) {Node<T>* new_node = new Node<T>(value);new_node->next = head.load();while (!head.compare_exchange_weak(new_node->next, new_node)) {// CAS失敗,說明有其他線程修改了head// 重新加載head并嘗試}}// 出棧操作bool pop(T& value) {Node<T>* old_head = head.load();while (old_head != nullptr) {// 嘗試將head指向下一個節點if (head.compare_exchange_weak(old_head, old_head->next)) {value = old_head->data;delete old_head;return true;}}return false; // 棧為空}
};

2.3 ABA問題處理

上述實現存在ABA問題,改進版使用帶標記的指針:

template<typename T>
class LockFreeStack {
private:struct TaggedPointer {Node<T>* ptr;uint64_t tag;bool operator==(const TaggedPointer& other) const {return ptr == other.ptr && tag == other.tag;}};std::atomic<TaggedPointer> head;public:void push(const T& value) {Node<T>* new_node = new Node<T>(value);TaggedPointer old_head = head.load();do {new_node->next = old_head.ptr;TaggedPointer new_head = {new_node, old_head.tag + 1};} while (!head.compare_exchange_weak(old_head, new_head));}bool pop(T& value) {TaggedPointer old_head = head.load();while (old_head.ptr != nullptr) {TaggedPointer new_head = {old_head.ptr->next, old_head.tag + 1};if (head.compare_exchange_weak(old_head, new_head)) {value = old_head.ptr->data;delete old_head.ptr;return true;}}return false;}
};

三、無鎖隊列實現

3.1 Michael-Scott無鎖隊列

template<typename T>
class LockFreeQueue {
private:struct Node {T data;std::atomic<Node*> next;Node() : next(nullptr) {}Node(const T& value) : data(value), next(nullptr) {}};std::atomic<Node*> head;std::atomic<Node*> tail;public:LockFreeQueue() {Node* dummy = new Node();head.store(dummy);tail.store(dummy);}~LockFreeQueue() {T value;while (pop(value));delete head.load();}// 入隊操作void enqueue(const T& value) {Node* new_node = new Node(value);Node* old_tail = tail.load();while (true) {// 嘗試將新節點連接到隊尾if (old_tail->next.compare_exchange_weak(nullptr, new_node)) {// 成功連接,嘗試更新tail指針tail.compare_exchange_strong(old_tail, new_node);return;} else {// 連接失敗,說明有其他線程已經更新了tailtail.compare_exchange_strong(old_tail, old_tail->next);old_tail = tail.load();}}}// 出隊操作bool dequeue(T& value) {Node* old_head = head.load();while (true) {Node* next = old_head->next.load();if (next == nullptr) {return false; // 隊列為空}// 嘗試更新head指針if (head.compare_exchange_weak(old_head, next)) {value = next->data;delete old_head; // 釋放原頭節點(dummy或前一個節點)return true;}}}
};

四、CAS無鎖算法的性能考量

4.1 優勢

  • 無阻塞:線程不會因競爭而阻塞,減少上下文切換開銷
  • 細粒度并發:允許多個線程同時操作不同部分的數據結構
  • 避免死鎖:無需獲取鎖,從根本上消除死鎖風險

4.2 劣勢

  • 高競爭下性能下降:大量CAS失敗導致頻繁重試
  • 實現復雜度高:需要處理ABA問題、內存回收等復雜問題
  • 調試困難:非確定性的執行順序增加調試難度

4.3 適用場景

  • 高并發低競爭:線程間沖突較少的場景
  • 實時系統:不允許長時間阻塞的場景
  • 高性能核心組件:如網絡框架、數據庫引擎

五、內存回收方案

無鎖數據結構的一個關鍵挑戰是安全地回收內存,避免"懸垂指針"問題。常見方案有:

5.1 Hazard Pointer

// 簡化版Hazard Pointer實現
template<typename T>
class HazardPointer {
private:static constexpr int MAX_THREADS = 64;std::atomic<void*> hazard_pointers[MAX_THREADS];std::atomic<int> thread_count;thread_local static int thread_id;public:void* get_hazard_pointer() {if (thread_id == -1) {thread_id = thread_count.fetch_add(1);}return hazard_pointers[thread_id].load();}void set_hazard_pointer(void* ptr) {hazard_pointers[thread_id].store(ptr);}void clear_hazard_pointer() {hazard_pointers[thread_id].store(nullptr);}bool can_reclaim(Node<T>* node) {for (int i = 0; i < thread_count; ++i) {if (hazard_pointers[i].load() == node) {return false;}}return true;}
};

5.2 延遲刪除

// 簡化版延遲刪除實現
template<typename T>
class LockFreeStack {
private:std::atomic<Node<T>*> head;std::atomic<int> delete_count;std::vector<Node<T>*> to_be_deleted;const int BATCH_SIZE = 100;public:bool pop(T& value) {Node<T>* old_head = head.load();while (old_head != nullptr) {if (head.compare_exchange_weak(old_head, old_head->next)) {value = old_head->data;// 延遲刪除to_be_deleted.push_back(old_head);if (++delete_count >= BATCH_SIZE) {perform_deletion();}return true;}}return false;}void perform_deletion() {std::vector<Node<T>*> local;local.swap(to_be_deleted);delete_count = 0;for (auto node : local) {delete node;}}
};

六、總結與最佳實踐

  1. 優先使用標準庫實現:C++標準庫提供了許多無鎖容器,如std::atomicstd::queue的并發版本
  2. 謹慎使用無鎖結構:僅在性能關鍵且鎖競爭激烈的場景使用
  3. 解決ABA問題:使用帶版本號的指針或Hazard Pointer
  4. 注意內存回收:避免使用裸指針,采用安全的內存回收機制
  5. 充分測試:無鎖算法的正確性依賴于原子操作的順序,需進行充分測試

無鎖數據結構是一把雙刃劍,正確使用能帶來顯著性能提升,但實現難度較大。開發者需根據具體場景權衡利弊,選擇合適的實現方案。隨著硬件和編程語言的發展,無鎖編程將在高性能領域發揮越來越重要的作用。

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

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

相關文章

【數字圖像處理】

數字圖像處理 緒論1. 數字圖像處理基本概念2. 數字圖像處理系統的組成3. 數字圖像處理技術研究的內容4. 數字圖像處理技術的應用領域5. 圖像處理技術涉及的學科領域 圖像處理基礎1. 電磁波譜與可見光譜2. 人眼的亮度視覺特性3. 圖像的表示4. 空間分辨率和灰度級分辨率5. 像素間…

linux chrome瀏覽器打不開了

報錯信息 通過terminal執行google-chrome [12714:12714:0706/223620.723519:ERROR:chrome/browser/process_singleton_posix.cc:358] The profile appears to be in use by another Google Chrome process (54949) on another computer (192.168.0.17). Chrome has locked t…

Python:模塊

一、Python模塊基礎概念 1. 什么是Python模塊&#xff1f; 在 Python 中&#xff0c;模塊&#xff08;Module&#xff09; 是一個包含 Python 代碼的文件&#xff08;擴展名為 .py&#xff09;&#xff0c;用于組織代碼、實現功能復用和命名空間管理。模塊可以包含變量、函數…

C 語言指針與作用域詳解

一、指針基礎概念 &#xff08;一&#xff09;指針的本質 指針是 C 語言中一個重要的概念&#xff0c;其本質是內存地址。在計算機內存中&#xff0c;每個字節都有唯一的編號&#xff0c;這個編號就是我們所說的內存地址&#xff0c;而指針變量就是用于存儲這些內存地址的變量…

解鎖阿里云ACK:開啟Kubernetes容器化應用新時代

引言&#xff1a;云原生時代下的 ACK 在當今數字化飛速發展的時代&#xff0c;云原生技術正以前所未有的速度改變著軟件開發和部署的格局。隨著企業對應用敏捷性、彈性擴展以及成本優化的需求日益增長&#xff0c;云原生已成為眾多企業實現數字化轉型的關鍵路徑。在云原生的技…

【C++基礎】內存管理四重奏:malloc/free vs new/delete - 面試高頻考點與真題解析

在 C/C 編程中&#xff0c;內存管理是核心基礎技能&#xff0c;而malloc/free和new/delete作為兩套內存分配釋放機制&#xff0c;是面試中高頻出現的考點。 一、內存管理的 "雙生花"&#xff1a;基礎概念解析 1.1 malloc/free&#xff1a;C 語言的內存管家 malloc全…

Dify+Ollama+QwQ:3步本地部署,開啟AI搜索新篇章

如何來評價本地化部署的價值與優勢分析&#xff1a; 成本優化與隱私保障 自定義搜索插件&#xff0c;告別信息過載 一鍵生成報告、分析&#xff0c;效率翻倍&#xff01; 接下來我們就嘗試跟隨來部署本地的價值所在! 1&#xff1a;安裝Ollama & 部署QwQ模型 1.1 安裝O…

FAISS 簡介及其與 GPT 的對接(RAG)

什么是 FAISS&#xff1f; FAISS (Facebook AI Similarity Search) 是 Facebook AI 團隊開發的一個高效的相似性搜索和密集向量聚類的庫。它主要用于&#xff1a; 大規模向量相似性搜索高維向量最近鄰檢索向量聚類 https://github.com/facebookresearch/faissFAISS 特別適合處理…

【Apache Doris 深度實戰:從 MPP 架構到實時分析,解鎖三大數據模型的性能優化秘籍】

一、安裝部署 安裝教程&#xff1a;GitHub地址 Doc文檔&#xff1a;Apache Doris 簡介 - Apache Doris 二、功能及作用 Apache Doris 是一款基于MPP 架構的高性能、實時分析型數據庫。它以高效、簡單和統一的特性著稱&#xff0c;能夠在亞秒級的時間內返回海量數據的查詢結果…

MySQL主從復制與讀寫分離概述

前言&#xff1a; 在數據驅動的現代應用中&#xff0c;數據庫面臨高并發讀寫與海量存儲的雙重挑戰。單一數據庫實例在性能、可用性及擴展性上逐漸成為瓶頸。MySQL主從復制&#xff08;Master-Slave Replication&#xff09;與讀寫分離&#xff08;Read/Write Splitting&#xf…

數據庫-元數據表

1. 什么是元數據表元數據&#xff1a;數據的數據&#xff0c;用以描述數據的信息也是數據&#xff0c;被稱為元數據2. 獲取元數據的方法MySQL提供了以下三種方法用于獲取數據庫對象的元數據&#xff1a;show語句從INFORMATION_SCHEMA數據庫里查詢相關表&#xff08;information…

【STM32】通用定時器PWM

STM32 通用定時器 PWM 輸出完全解析&#xff08;以 TIM3_CH1 為例&#xff09; PWM 輸出基本原理 PWM&#xff08;Pulse Width Modulation&#xff09;即脈沖寬度調制&#xff0c;是由定時器通過比較 CNT 與 CCR 寄存器實現的。 信號產生原理&#xff1a; ARR 決定周期&#…

python學習打卡:DAY 21 常見的降維算法

知識點回顧&#xff1a; LDA線性判別PCA主成分分析t-sne降維 還有一些其他的降維方式&#xff0c;也就是最重要的詞向量的加工&#xff0c;我們未來再說 浙大疏錦行

基于SpringBoot和Leaflet集成在線天氣服務的區縣當前天氣WebGIS實戰

目錄 前言 一、需求描述 1、功能需求 2、技術實現流程 二、SpringBoot后臺實現 1、控制層實現 2、區縣數據返回 三、WebGIS前端實現 1、區位信息展示 2、天氣信息展示 四、成果展示 1、魔都上海 2、蜀地成都 3、湖南桂東 五、總結 前言 在當今數字化時…

文心開源:文心大模型4.5系列全面開放,AI普惠時代加速到來

一場由4240億參數模型領銜的開源盛宴&#xff0c;正在重塑中國AI生態的底層邏輯 2025年6月30日&#xff0c;百度如約宣布全面開源其旗艦產品——文心大模型4.5系列。一次性開源10款模型&#xff0c;覆蓋從4240億參數的MoE多模態巨無霸到輕巧的0.3B端側模型&#xff0c;并同步開…

【運算放大器專題】基礎篇

1.1 運算放大器是放大了個寂寞嗎&#xff1f;—初識運算放大器 為了解決震蕩問題&#xff0c;人為加了一些補償網絡之后導致的高頻特性差 1.2歐姆定律和獨立源 1正弦2方波3脈沖 電壓源是平行于i軸的橫線 1.3有伴源和運放緩沖器 有伴指的是有電阻&#xff0c;有伴是壞事&#…

英偉達 jetson nano 從NFS啟動,使用英偉達提供的rootfs根文件系統

0、目標 為了方便驅動階段的開發&#xff0c;并且使用英偉達提供的上層應用&#xff0c;這里希望使jetson nano 從NFS啟動&#xff0c;同時使用英偉達提供的rootfs根文件系統。 1、硬件準備 確保jetson nano 板子和開發主機之間使用網線進行連接&#xff08;保持板子和開發主…

廣州華銳互動:以創新科技賦能教育,開啟沉浸式學習?

在教育領域&#xff0c;廣州華銳互動致力于打破傳統教學的局限性&#xff0c;為師生們帶來全新的沉浸式學習體驗。廣州華銳互動通過開發 VR 虛擬教學課件&#xff0c;將抽象的知識轉化為生動、逼真的虛擬場景&#xff0c;讓學生能夠身臨其境地感受知識的魅力 。比如在歷史課上&…

Grok 4 最新技術評測與發布指南

TL;DR&#xff1a;馬斯克跳過Grok 3.5直接發布Grok 4&#xff0c;計劃在7月4日后上線&#xff0c;專注編程模型優化&#xff0c;這次"極限迭代"能否讓馬斯克在AI軍備競賽中翻盤&#xff1f; &#x1f4cb; 文章目錄 &#x1f680; Grok 4發布概況&#x1f3c6; Grok…

為什么音視頻通話需要邊緣加速

? 主要原因 ? 降低傳輸延遲 用戶與邊緣節點之間通常1-2跳即可完成連接&#xff0c;避免跨國、跨運營商長鏈路傳輸 保障音視頻信令、媒體流快速到達&#xff0c;控制端到端延遲 ? 提升弱網環境下的連接穩定性 邊緣節點具備鏈路優化、丟包補償、轉發中繼功能 即使在WiFi切…