并發 -- 無鎖算法與結構

文章目錄

      • 什么是無鎖算法
      • 什么是原子變量
      • 什么是CAS操作
      • Compare-And-Swap Weak在哪些情況下會失敗
      • 舉例說明無鎖結構
      • 無鎖結構的問題

什么是無鎖算法

無鎖算法(Lock-Free Algorithm)是一種并發編程技術,旨在實現多線程環境下的高效數據共享,而無需使用傳統的鎖機制(如互斥鎖)。
關鍵特性或優點:

  1. 無阻塞:至少有一個線程能在有限步驟內完成操作,確保系統整體不會因鎖爭用而停滯。
  2. 無死鎖:由于不使用鎖,避免了死鎖問題。
  3. 高并發性:適用于多核處理器,多個線程可同時操作共享數據,減少等待時間。

在C++ 可以使用原子變量來實現無鎖算法與結構

什么是原子變量

C++的原子變量(Atomic Variable)是通過std::atomic模板類提供的,用于在多線程環境中安全地操作共享數據
模板原形

// Defined in header <atomic>
template< class T >
struct atomic;  // (1)	(since C++11)
template< class U >
struct atomic<U*>; //(2)	(since C++11)
// Defined in header <memory>
template< class U >
struct atomic<std::shared_ptr<U>>;  //(3)	(since C++20)
template< class U >
struct atomic<std::weak_ptr<U>>; // (4)	(since C++20)
Defined in header <stdatomic.h>
#define _Atomic(T) /* see below */  // (5)	(since C++23)

具體參考cppreference atomic

下面是一個使用atomic_flag 實現自旋鎖的例子

class SpinLock {std::atomic_flag flag(ATOMIC_FLAG_INIT);
public:void lock(){while(flag.test_and_set(std::memory_and_acquire)){}}void unlock(){flag.clear(std::memory_and_release);}};

其中std::memory_and_releasestd::memory_and_acquire的含義需自行查找內存模型與順序

使用atomic 操作就是沒有顯示的使用鎖操作,但atomic內部可能是使用鎖機制來實現的也可能是使用cpu的CAS操作來實現的
通過atomic::is_lock_free可以查看atomic內部是不是無鎖機制實現的

#include <atomic>
#include <iostream>
#include <utility>struct A { int a[100]; };
struct B { int x, y; };
struct C { int x, y, z; };int main()
{std::cout << std::boolalpha<< "std::atomic_char is lock free? "<< std::atomic_char{}.is_lock_free() << '\n'<< "std::atomic_uintmax_t is lock free? "<< std::atomic_uintmax_t{}.is_lock_free() << '\n'<< "std::atomic<A> is lock free? "<< std::atomic<A>{}.is_lock_free() << '\n'<< "std::atomic<B> is lock free? "<< std::atomic<B>{}.is_lock_free() << '\n'<< "std::atomic<C> is lock free? "<< std::atomic<C>{}.is_lock_free() << '\n';
}

是與類型的長度有關的

什么是CAS操作

CAS(Compare-And-Swap,比較并交換)是一種原子操作,用于在多線程環境中實現無鎖同步。CAS操作會檢查某個內存位置的值是否與預期值匹配,如果匹配,則將其更新為新值;否則,不進行任何操作。無論是否更新,CAS操作都會返回內存位置的原始值。

CAS 操作包含三個操作數:

  1. 內存地址:需要修改的共享變量的地址。
  2. 期望值:當前線程認為該變量應該具有的值。
  3. 新值:如果變量的當前值等于期望值,則將其更新為新值。

CAS 的工作流程:

  1. 讀取內存地址中的當前值。
  2. 比較當前值與期望值:
    • 如果相等,則將新值寫入內存地址。
    • 如果不相等,則不做任何操作。
  3. 返回操作是否成功(通常返回當前值或布爾值)。

CAS 的硬件支持:

  • x86:CMPXCHG 指令(單字 CAS),CMPXCHG16B 指令(雙字 CAS)。
  • ARM:LDREX 和 STREX 指令(加載-存儲獨占指令)。
  • 其他架構:大多數現代 CPU 都提供了類似的原子指令。

以下是一個使用 CMPXCHG 實現原子操作的 C++ 示例:

#include <iostream>
#include <atomic>// 使用 CMPXCHG 實現原子 CAS 操作
bool atomic_compare_exchange(int* dest, int expected, int new_value) {int result;__asm__ volatile ("lock cmpxchgl %3, %1;"  // lock 前綴確保原子性,cmpxchgl 是 32 位 CMPXCHG: "=a" (result), "+m" (*dest)  // 輸出:result = EAX,dest 是內存操作數: "a" (expected), "r" (new_value)  // 輸入:EAX = expected,new_value 是寄存器操作數: "memory"  // 告訴編譯器內存可能被修改);return result == expected;  // 返回是否成功
}int main() {int shared_value = 10;int expected = 10;int new_value = 20;std::cout << "Initial value: " << shared_value << std::endl;// 嘗試原子地將 shared_value 從 expected (10) 更新為 new_value (20)bool success = atomic_compare_exchange(&shared_value, expected, new_value);if (success) {std::cout << "CAS succeeded. New value: " << shared_value << std::endl;} else {std::cout << "CAS failed. Value is still: " << shared_value << std::endl;}return 0;
}

缺點:

  1. ABA 問題:變量的值從 A 變為 B 又變回 A,CAS 無法檢測到中間變化。
  2. 忙等待:在高競爭場景下,CAS 可能導致大量重試,浪費 CPU 資源。
  3. 復雜性:實現無鎖數據結構需要復雜的邏輯和嚴格的正確性驗證。

CAS操作的變體:

  1. Compare-And-Swap Weak:允許在某些情況下失敗(如虛假失敗),但性能更高。
  2. Compare-And-Swap Strong:確保只有在值不匹配時才失敗,性能較低但更可靠。

Compare-And-Swap Weak在哪些情況下會失敗

  1. 當前值與預期值不匹配
  2. 內存問題:
    • 并發競爭 , 如果多個線程同時嘗試修改同一個內存地址,CAS Weak 可能會因為競爭而失敗。即使當前值與預期值相等,其他線程的干擾也可能導致操作失敗。
    • 內存訪問沖突:如果內存訪問沖突頻繁發生,硬件可能會放棄 CAS Weak 操作,導致失敗。
    • 在某些弱內存模型(如 ARM 或 PowerPC)中,指令重排序可能會導致 CAS Weak 失敗。例如,內存屏障未正確設置時,CAS Weak 可能會觀察到不一致的內存狀態。
    • 如果系統資源(如緩存、內存帶寬)緊張,CAS Weak 可能會因為資源競爭而失敗。
    • 緩存一致性協議的影響:在某些多核處理器架構中,緩存一致性協議(如 MESI 協議)可能導致 CAS Weak 失敗。例如,當多個核心同時競爭同一內存地址時,緩存行的狀態可能會發生變化,從而導致 CAS Weak 失敗

Compare-And-Swap Weak 的偽代碼實現

template<T>
bool cas_weak(T* dest, T& expected,const T& new_value)
{if (*dest == expected) {if (try_update(dest,new_value)){return true;} else {return false;}} else {expected = *dest;return false}
}

舉例說明無鎖結構

無鎖鏈表

#include <atomic>
#include <memory>
#include <iostream>template <typename T>
class LockFreeLinkedList {
private:struct Node {std::shared_ptr<T> data; // 存儲數據std::atomic<Node*> next; // 指向下一個節點的原子指針Node(T const& value) : data(std::make_shared<T>(value)), next(nullptr) {}};std::atomic<Node*> head; // 鏈表頭指針public:LockFreeLinkedList() : head(nullptr) {}~LockFreeLinkedList() {// 析構時釋放所有節點Node* current = head.load();while (current) {Node* next = current->next.load();delete current;current = next;}}// 插入操作void insert(T const& value) {Node* new_node = new Node(value); // 創建新節點new_node->next = head.load();    // 新節點的 next 指向當前頭節點// CAS 操作:將 head 從當前值更新為新節點while (!head.compare_exchange_weak(new_node->next, new_node)) {// 如果 CAS 失敗,說明 head 已經被其他線程修改,重新嘗試}}// 刪除操作bool remove(T const& value) {Node* prev = head.load(); // 前驅節點Node* curr = prev;        // 當前節點while (curr) {Node* next = curr->next.load(); // 下一個節點// 如果當前節點的值匹配if (curr->data && *curr->data == value) {// CAS 操作:將前驅節點的 next 從當前節點更新為下一個節點if (prev == curr) {// 如果當前節點是頭節點if (head.compare_exchange_weak(curr, next)) {delete curr; // 刪除節點return true;}} else {if (prev->next.compare_exchange_weak(curr, next)) {delete curr; // 刪除節點return true;}}}// 移動指針prev = curr;curr = next;}return false; // 未找到匹配的節點}// 打印鏈表內容(用于調試)void print() const {Node* current = head.load();while (current) {if (current->data) {std::cout << *current->data << " -> ";}current = current->next.load();}std::cout << "nullptr" << std::endl;}
};

無鎖結構的問題

  1. ABA 問題:在無鎖數據結構中,ABA 問題是一個常見的挑戰。可以通過使用帶有版本號的指針(如 std::atomic<std::shared_ptr>)或雙字 CAS(Compare-And-Swap)來解決。
  2. 內存管理:無鎖數據結構中的內存管理需要特別小心,因為多個線程可能同時訪問和釋放內存。通常使用智能指針(如 std::shared_ptr)來管理內存。
  3. 性能測試:無鎖數據結構的性能在高并發環境下可能優于基于鎖的數據結構,但在低并發環境下可能表現不佳。因此,在實際應用中需要進行充分的性能測試。

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

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

相關文章

考研/保研復試英語問答題庫(華工建院)

華南理工大學建筑學院保研/考研 英語復試題庫&#xff0c;由華工保研er和學碩筆試第一同學一起整理&#xff0c;覆蓋面廣&#xff0c;助力考研/保研上岸&#xff01;需要&#x1f447;載可到文章末尾見小&#x1f360;。 以下是主要內容&#xff1a; Part0 復試英語的方法論 Pa…

岳陽市美術館預約平臺(小程序論文源碼調試講解)

第4章 系統設計 一個成功設計的系統在內容上必定是豐富的&#xff0c;在系統外觀或系統功能上必定是對用戶友好的。所以為了提升系統的價值&#xff0c;吸引更多的訪問者訪問系統&#xff0c;以及讓來訪用戶可以花費更多時間停留在系統上&#xff0c;則表明該系統設計得比較專…

Python游戲編程之賽車游戲6-3

1 “敵人”汽車類的創建 在創建玩家汽車類之后&#xff0c;接下來創建“敵人”汽車類。“敵人”汽車類與玩家類一樣&#xff0c;也是包含兩個方法&#xff0c;一個是__init__()&#xff0c;另一個是move()。 1.1 __init__()方法 “敵人”汽車類的__init__()方法代碼如圖1所示…

TCP/UDP調試工具推薦:Socket通信圖解教程

TCP/UDP調試工具推薦&#xff1a;Socket通信圖解教程 一、引言二、串口調試流程三、下載鏈接 SocketTool 調試助手是一款旨在協助程序員和網絡管理員進行TCP和UDP協議調試的網絡通信工具。TCP作為一種面向連接、可靠的協議&#xff0c;具有諸如連接管理、數據分片與重組、流量和…

神經網絡 - 神經元

人工神經元(Artificial Neuron)&#xff0c;簡稱神經元(Neuron)&#xff0c;是構成神經網絡的基本單元&#xff0c;其主要是模擬生物神經元的結構和特性&#xff0c;接收一組輸入信號并產生輸出。 生物學家在 20 世紀初就發現了生物神經元的結構。一個生物神經元通常具有多個樹…

藍橋杯備考:貪心算法之矩陣消除游戲

這道題是牛客上的一道題&#xff0c;它呢和我們之前的排座位游戲非常之相似&#xff0c;但是&#xff0c;排座位問題選擇行和列是不會改變元素的值的&#xff0c;這道題呢每每選一行都會把這行或者這列清零&#xff0c;所以我們的策略就是先用二進制把選擇所有行的情況全部枚舉…

DeepSeek系統架構的逐層分類拆解分析,從底層基礎設施到用戶端分發全鏈路

一、底層基礎設施層 1. 硬件服務器集群 算力單元&#xff1a; GPU集群&#xff1a;基于NVIDIA H800/H100 GPU構建&#xff0c;單集群規模超10,000卡&#xff0c;采用NVLink全互聯架構實現低延遲通信。國產化支持&#xff1a;適配海光DCU、寒武紀MLU等國產芯片&#xff0c;通過…

ktransformers 上的 DeepSeek-R1 671B open-webui

ktransformers 上的 DeepSeek-R1 671B open-webui 一、下載GGUF模型1. 創建目錄2. 魔塔下載 DeepSeek-R1-Q4_K_M3. 安裝顯卡驅動和cuda4. 顯卡 NVIDIA GeForce RTX 4090 二、安裝ktransformers1. 安裝依賴2. 安裝uv工具鏈3. 下載源碼4. 創建python虛擬環境 三、編譯ktransforme…

smolagents學習筆記系列(五)Tools-in-depth-guide

這篇文章鎖定官網教程中的 Tools-in-depth-guide 章節&#xff0c;主要介紹了如何詳細構造自己的Tools&#xff0c;在之前的博文 smolagents學習筆記系列&#xff08;二&#xff09;Agents - Guided tour 中我初步介紹了下如何將一個函數或一個類聲明成 smolagents 的工具&…

形式化數學編程在AI醫療中的探索路徑分析

一、引言 1.1 研究背景與意義 在數字化時代,形式化數學編程和 AI 形式化醫療作為前沿領域,正逐漸改變著我們的生活和醫療模式。形式化數學編程是一種運用數學邏輯和嚴格的形式化語言來描述和驗證程序的技術,它通過數學的精確性和邏輯性,確保程序的正確性和可靠性。在軟件…

C#初級教程(3)——變量與表達式:從基礎到實踐

一、為什么使用變量 計算機程序本質上是對數據的操作&#xff0c;數字、文字、圖片等在計算機中都屬于數據。而變量&#xff0c;就是數據在計算機內存中的 “棲息地”。我們可以把變量想象成一個個小盒子&#xff0c;這些盒子能存放各種數據&#xff0c;需要時還能隨時取出。 二…

【深度學習神經網絡學習筆記(三)】向量化編程

向量化編程 向量化編程前言1、向量化編程2、向量化優勢3、正向傳播和反向傳播 向量化編程 前言 向量化編程是一種利用專門的指令集或并行算法來提高數據處理效率的技術&#xff0c;尤其在科學計算、數據分析和機器學習領域中非常常見。它允許通過一次操作處理整個數組或矩陣的…

海康威視攝像頭RTSP使用nginx推流到服務器直播教程

思路&#xff1a; 之前2020年在本科的時候&#xff0c;由于項目的需求需要將海康威視的攝像頭使用推流服務器到網頁進行直播。這里將自己半個月琢磨出來的步驟給大家發一些。切勿轉載&#xff01;&#xff01;&#xff01;&#xff01; 使用網絡攝像頭中的rtsp協議---------通…

鴻蒙開發深入淺出03(封裝通用LazyForEach實現懶加載)

鴻蒙開發深入淺出03&#xff08;封裝通用LazyForEach實現懶加載&#xff09; 1、效果展示2、ets/models/BasicDataSource.ets3、ets/models/HomeData.ets4、ets/api/home.ets5、ets/pages/Home.ets6、ets/views/Home/SwiperLayout.ets7、后端代碼 1、效果展示 2、ets/models/Ba…

【Rust中級教程】2.8. API設計原則之靈活性(flexible) Pt.4:顯式析構函數的問題及3種解決方案

喜歡的話別忘了點贊、收藏加關注哦&#xff08;加關注即可閱讀全文&#xff09;&#xff0c;對接下來的教程有興趣的可以關注專欄。謝謝喵&#xff01;(&#xff65;ω&#xff65;) 說句題外話&#xff0c;這篇文章一共5721個字&#xff0c;是我截至目前寫的最長的一篇文章&a…

一周學會Flask3 Python Web開發-Jinja2模板過濾器使用

鋒哥原創的Flask3 Python Web開發 Flask3視頻教程&#xff1a; 2025版 Flask3 Python web開發 視頻教程(無廢話版) 玩命更新中~_嗶哩嗶哩_bilibili 在Jinja2中&#xff0c;過濾器(filter)是一些可以用來修改和過濾變量值的特殊函數&#xff0c;過濾器和變量用一個豎線 | &a…

數據庫 安裝initializing database不通過

出現一下情況時&#xff1a; 處理方法&#xff1a; 將自己的電腦名稱 中文改成英文 即可通過

嵌入式開發:傅里葉變換(5):STM32和Matlab聯調驗證FFT

目錄 1. MATLAB獲取 STM32 的原始數據 2. 將數據上傳到電腦 3. MATLAB 接收數據并驗證 STM32進行傅里葉代碼 結果分析 STM32 和 MATLAB 聯調是嵌入式開發中常見的工作流程&#xff0c;通常目的是將 STM32 采集的數據或控制信號傳輸到 MATLAB 中進行實時處理、分析和可視化…

Mobaxterm服務器常用命令(持續更新)

切換文件夾 cd path # for example, cd /gpu03/deeplearning/進入不同GPU ssh mgmt ssh gpu01 ssh gpu03尋找文件位置 find /path -name file_name #for example, find / -name lib #在根目錄下搜尋名為lib文件 #for example, find /home/deeplearning -name "lib"…

MFC文件和注冊表的操作

MFC文件和注冊表的操作 日志、操作配置文件、ini、注冊表、音視頻的文件存儲 Linux下一切皆文件 C/C操作文件 const char* 與 char* const const char* 常量指針&#xff0c;表示指向的內容為常量。指針可以指向其他變量&#xff0c;但是內容不能再變了 char szName[6]&qu…