C++ 并發編程:全面解析主流鎖管理類

在 C++ 的并發世界里,管理共享資源就像是在一個繁忙的十字路口指揮交通。如果指揮不當,就會發生混亂甚至致命的“死鎖”。C++ 標準庫提供的各種鎖管理工具,就是我們手中的“交通信號燈”,它們各自擁有獨特的職能,幫助我們編寫出安全、高效且優雅的多線程代碼。

1. std::lock_guard:忠誠的衛士

std::lock_guard 是最基礎、最可靠的守衛。它就像一個忠誠的士兵,一旦被部署(構造),就會牢牢地守住陣地(鎖定互斥量),直到任務完成(超出作用域)自動卸下職責。它從不偷懶,也從不犯錯,無論程序是正常退出還是因異常而中斷,它都確保鎖被安全釋放。

它的優勢在于簡單而純粹:你只需要告訴它要守護哪個互斥量,剩下的它都會為你自動完成。在你的代碼中,如果只需要在一個作用域內獨占訪問一個資源,lock_guard 永遠是你的首選,因為它沒有多余的開銷,也不給你犯錯的機會。

  • 核心特性

    • 自動加鎖與解鎖:遵循 RAII 原則,生命周期與作用域綁定。
    • 不可移動、不可拷貝:確保鎖的所有權唯一。
    • 無死鎖避免:如果你需要鎖定多個互斥量,必須手動確保所有線程都以相同的順序加鎖,否則可能發生死鎖。
  • 適用場景

    • 當你需要在一個函數或一個代碼塊中安全地鎖定一個互斥量時。這是最常見的獨占鎖使用模式。
  • 示例

    #include <iostream>
    #include <thread>
    #include <mutex>
    #include <vector>std::mutex mtx;
    int shared_data = 0;void safe_increment() {// 創建 lock_guard 對象,mtx 在這里被鎖定std::lock_guard<std::mutex> lock(mtx);// 在此作用域內安全訪問共享資源shared_data++;// 當 lock 超出作用域,mtx 會自動解鎖
    } // 這里會自動調用 lock.unlock()int main() {std::vector<std::thread> threads;for (int i = 0; i < 10; ++i) {threads.emplace_back(safe_increment);}for (auto& t : threads) {t.join();}std::cout << "最終 shared_data 的值: " << shared_data << std::endl;return 0;
    }
    

2. std::unique_lock:全能的指揮官

std::unique_lock 是一位能力超群的指揮官。它擁有 lock_guard 的所有優點,但其最大的特點是靈活。它不像 lock_guard 那樣死板,你可以在任何時候手動加鎖、解鎖,甚至決定在創建時延遲加鎖。這種靈活性使得它能應對更復雜的戰術。

unique_lock 的真正力量體現在與 std::condition_variable 的協同作戰中。在等待某個條件時,unique_lock 可以優雅地放下手中的鎖,讓出資源,直到被通知時再迅速重新鎖定。這種合作機制是實現生產者-消費者模型、線程池等高級并發模式的核心。

  • 核心特性

    • 靈活的加鎖/解鎖控制:提供 lock()unlock()try_lock() 等成員函數。
    • 可延遲加鎖:通過 std::defer_lock 構造,創建對象時不立即加鎖。
    • 可移動:可以作為函數參數或返回值,將鎖的所有權轉移。
    • 與條件變量配合:是 std::condition_variable::wait() 函數唯一接受的鎖類型。
  • 適用場景

    • 當你需要手動控制鎖的加鎖和解鎖時。
    • 當你需要與 std::condition_variable 配合,實現等待/通知機制時。
    • 當你需要將鎖的所有權從一個函數轉移到另一個函數時。
  • 示例

    #include <iostream>
    #include <thread>
    #include <mutex>
    #include <condition_variable>std::mutex mtx;
    std::condition_variable cv;
    bool ready = false;void worker_thread() {// 創建 unique_lock 對象,并鎖定互斥量std::unique_lock<std::mutex> lock(mtx);std::cout << "工作線程正在等待條件..." << std::endl;// 等待條件變為 true,wait() 會原子地釋放鎖并進入休眠cv.wait(lock, []{ return ready; }); // 被喚醒后,wait() 會自動重新鎖定互斥量std::cout << "工作線程被喚醒,開始處理數據。" << std::endl;
    }void main_thread() {std::this_thread::sleep_for(std::chrono::milliseconds(100));// 創建 unique_lock 并鎖定互斥量std::unique_lock<std::mutex> lock(mtx);ready = true;std::cout << "主線程設置條件,并通知工作線程。" << std::endl;// 必須在通知前釋放鎖,以允許被喚醒的線程獲取它lock.unlock(); cv.notify_one();
    }int main() {std::thread t1(worker_thread);std::thread t2(main_thread);t1.join();t2.join();return 0;
    }
    

3. std::scoped_lock:智慧的協調者

std::scoped_lock 是 C++17 引入的,它的主要作用是簡化多互斥量加鎖,并使用內置的死鎖避免算法。你可以把它看作是 std::lock_guard 的多功能升級版。

  • 核心特性

    • 同時鎖定一個或多個互斥量
    • 內置死鎖避免算法:它會以原子方式嘗試鎖定所有互斥量,如果失敗則回滾并重試,確保不會因鎖順序不一致而死鎖。
    • 無手動控制:和 lock_guard 一樣,不能手動加鎖或解鎖。
  • 適用場景

    • 當你需要同時鎖定多個互斥量時,這是最安全、最方便的選擇。它徹底消除了死鎖的風險。
  • 示例

    #include <iostream>
    #include <thread>
    #include <mutex>std::mutex mtx1;
    std::mutex mtx2;void transfer_data_safe(int from_id, int to_id) {std::cout << "線程 " << std::this_thread::get_id() << " 正在嘗試數據傳輸..." << std::endl;// 一次性鎖定 mtx1 和 mtx2,使用內置的死鎖避免算法// 無論哪個線程先獲得哪個鎖,都保證不會發生死鎖std::scoped_lock lock(mtx1, mtx2);// 模擬數據傳輸std::this_thread::sleep_for(std::chrono::milliseconds(50));std::cout << "線程 " << std::this_thread::get_id() << " 安全地完成了數據傳輸。" << std::endl;
    } // lock 超出作用域,mtx1 和 mtx2 自動解鎖int main() {std::thread t1(transfer_data_safe, 1, 2);std::thread t2(transfer_data_safe, 2, 1);t1.join();t2.join();return 0;
    }
    

4. std::shared_lock:高效的圖書館管理員

std::shared_lock 是 C++14 引入的,它與 std::shared_mutex(也叫讀寫鎖)配合使用。它的主要作用是管理共享鎖的所有權,提供一種允許多個線程同時讀取,但只允許一個線程寫入的同步機制。

  • 核心特性

    • 共享鎖模式:允許多個線程同時持有鎖,用于并發讀取。
    • RAII 風格:在構造時加鎖,在析構時自動解鎖。
    • 必須與 std::shared_mutex 配合使用
  • 適用場景

    • 讀多寫少的場景。當讀取數據的頻率遠高于寫入數據時,使用 shared_lock 可以顯著提高程序的并發性能。
  • 示例

    #include <iostream>
    #include <thread>
    #include <shared_mutex>
    #include <vector>
    #include <string>std::string shared_data = "initial";
    std::shared_mutex shared_mtx;// 讀者線程:只讀取數據,可以并發執行
    void reader_thread(int id) {// 構造 shared_lock 時,請求共享鎖std::shared_lock<std::shared_mutex> lock(shared_mtx);std::cout << "讀者 " << id << " 正在讀取: " << shared_data << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(20));
    } // lock 超出作用域,自動釋放共享鎖// 寫者線程:修改數據,獨占訪問
    void writer_thread(const std::string& new_data) {// 構造 unique_lock 時,請求獨占鎖,會阻塞所有讀者和寫者std::unique_lock<std::shared_mutex> lock(shared_mtx);std::cout << "寫者正在寫入..." << std::endl;shared_data = new_data;std::this_thread::sleep_for(std::chrono::milliseconds(50));
    } // lock 超出作用域,自動釋放獨占鎖int main() {std::vector<std::thread> readers;for (int i = 0; i < 5; ++i) {readers.emplace_back(reader_thread, i);}std::thread writer1(writer_thread, "new data");for (int i = 5; i < 10; ++i) {readers.emplace_back(reader_thread, i);}writer1.join();for (auto& t : readers) {t.join();}std::cout << "最終數據是: " << shared_data << std::endl;return 0;
    }
    

5. std::lock:傳統的鎖定大師

std::lock 是一個函數,而不是一個類。它的主要作用是一次性鎖定多個互斥量,并使用內置的死鎖避免算法

  • 核心特性

    • 函數:不是 RAII 類,通常需要與 std::unique_lockstd::defer_lock 配合使用。
    • 死鎖避免:通過其內部的算法,確保無論傳入互斥量的順序如何,都能安全地加鎖。
  • 適用場景

    • 在 C++11/14 版本中,是處理多鎖死鎖問題的標準方法。在 C++17 及以后,通常被 std::scoped_lock 所取代。
  • 示例

    #include <iostream>
    #include <thread>
    #include <mutex>std::mutex mtxA;
    std::mutex mtxB;void process_data_lock_function() {std::cout << "線程 " << std::this_thread::get_id() << " 正在處理數據..." << std::endl;// 延遲加鎖,創建 unique_lock 對象但不立即鎖定互斥量std::unique_lock<std::mutex> lockA(mtxA, std::defer_lock);std::unique_lock<std::mutex> lockB(mtxB, std::defer_lock);// 使用 std::lock 函數一次性鎖定兩個互斥量std::lock(lockA, lockB);std::cout << "線程 " << std::this_thread::get_id() << " 已安全地獲取所有鎖。" << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(100));// lockA 和 lockB 超出作用域時會自動解鎖
    }int main() {std::thread t1(process_data_lock_function);std::thread t2(process_data_lock_function);t1.join();t2.join();return 0;
    }
    

總結對比

特性std::lock_guardstd::unique_lockstd::scoped_lockstd::shared_lock
鎖定類型獨占鎖獨占鎖獨占鎖共享鎖
加鎖數量111或多個1
靈活性最低最高中等
死鎖避免內置
與條件變量
主要用途簡單、安全的單鎖管理需要靈活控制鎖或與條件變量配合安全的多鎖管理讀多寫少場景

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

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

相關文章

Spring boot 啟用第二數據源

1. 數據源配置代碼&#xff1a;import com.alibaba.druid.pool.DruidDataSource; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilde…

Wi-Fi 時延與掉包的關鍵因素全解析

在無線網絡性能優化中&#xff0c;時延&#xff08;Latency&#xff09;與掉包&#xff08;Packet Loss&#xff09;是最核心的兩個指標。本文將從 物理層、MAC層、系統棧、業務形態與環境 等多個維度&#xff0c;對 Wi-Fi 時延與掉包的關鍵因素進行全面梳理&#xff0c;并結合…

《Python 文本分析實戰:從單詞頻率統計到高效可視化的全流程指南》

《Python 文本分析實戰:從單詞頻率統計到高效可視化的全流程指南》 一、引言:小任務背后的大世界 在我多年的開發與教學中,文本處理始終是一個繞不開的主題。無論是日志分析、自然語言處理,還是搜索引擎、推薦系統,幾乎所有數據驅動的系統都離不開對文本的理解。而“統計…

12KM無人機高清圖傳通信模組——打造未來空中通信新高度

在無人機技術飛速發展的今天&#xff0c;高清圖傳和穩定的通信模塊無疑是提高無人機作業效率和可靠性的關鍵因素。作為新一代無人機核心技術之一&#xff0c;深圳云望物聯12KM無人機高清圖傳通信模組憑借其卓越的性能&#xff0c;正逐步成為行業內的寵兒&#xff0c;成為無人機…

【LeetCode 熱題 100】62. 不同路徑——(解法二)遞推

Problem: 62. 不同路徑 文章目錄整體思路完整代碼時空復雜度時間復雜度&#xff1a;O(m * n)空間復雜度&#xff1a;O(m * n)整體思路 這段代碼同樣旨在解決 “不同路徑” 問題&#xff0c;但它采用的是一種 自底向上&#xff08;Bottom-Up&#xff09;的動態規劃 方法&#x…

C++ 高階錯誤解析:MSVC 與 Qt 全景指南

在 C 開發中&#xff0c;尤其是在 Windows 平臺使用 MSVC 或 Qt 框架 時&#xff0c;程序員經常會遇到編譯錯誤、鏈接錯誤和運行時異常。本文將系統梳理這些問題&#xff0c;按 語法錯誤、類型錯誤、鏈接錯誤、Qt 運行錯誤 分類&#xff0c;并給出 觸發示例、原因分析及修復策略…

基于Net海洋生態環境保護系統的設計與實現(代碼+數據庫+LW)

摘要 隨著全球氣候變化和人類活動的加劇&#xff0c;海洋生態系統面臨著前所未有的威脅。污染、過度捕撈、棲息地破壞等問題嚴重影響了海洋生物多樣性和生態平衡。為了應對海洋生態系統面臨的嚴重威脅&#xff0c;如污染、過度捕撈和棲息地破壞等問題&#xff0c;利用C#語言和…

DoIP路由激活報文

目錄 DoIP路由激活報文詳解 基本概念 報文結構 響應報文 通信流程 注意事項 **DoIP (Diagnostics over Internet Protocol) 報文詳解** **1. DoIP 報文結構** **1.1 通用報文格式** **2. 常見 DoIP 報文類型** **3. 典型 DoIP 報文示例** **3.1 車輛識別請求(廣播)** **3.2 車…

學習Python中Selenium模塊的基本用法(8:元素操作-2)

定位網頁元素后&#xff0c;調用is_displayed函數可以判斷元素的顯示狀態&#xff0c;如百度網站中有默認隱藏的元素&#xff0c;此時即可使用is_displayed函數判斷該元素的顯示狀態&#xff0c;如下面代碼所示&#xff1a;driver webdriver.Chrome() driver.get("https:…

雙指針:從「LC11 盛最多水的容器」到「LC42 接雨水」

LC11 盛最多水的容器 選擇兩條線&#xff0c;它們與x軸構成的容器可以盛的水量取決于兩條線中較短的那條以及兩條線之間的距離。 樸素的思想是使用i和j遍歷height中的所有線&#xff0c;但是這樣的時間復雜度是O(n2)O(n^2)O(n2)。 我們讓i從0開始&#xff0c;j從n-1開始&…

WINTRUST!_GetMessage函數分析之CRYPT32!CryptSIPGetSignedDataMsg函數的作用是得到nt5inf.cat的信息

UEDIT打開nt5inf.cat。第一部分&#xff1a;BOOL _GetMessage(CRYPT_PROVIDER_DATA *pProvData) {DWORD dwMsgEncoding;SIP_SUBJECTINFO *pSubjInfo;SIP_DISPATCH_INFO *pSip;DWORD cbEncodedMsg;BYTE *pbEncodedMsg;DWORD …

編譯esp32報錯解決辦法

報錯信息&#xff1a;CMake Error at build/CMakeFiles/git-data/grabRef.cmake:48 (file):file failed to open for reading (No such file or directory):這個錯誤是由于 Git 的安全檢查導致的。從錯誤信息可以看出&#xff0c;Git 檢測到了"可疑的所有權"&#xf…

【AI】常見8大LLM大語言模型地址

序號AI名稱地址1 ChatGPT &#xff08;OpenAI&#xff09;https://chat.openai.com/2Gemini (Google personal AI assistant)https://gemini.google.com/app3Grok (xAI Grok LLM)https://x.ai/4DeepSeek (DeepSeek AI chatbot)DeepSeek5Claude (Anthropic Claude AI)App unavai…

軟件系統的部署方式:單機、主備(冷主備、熱主備)、集群

一、單機部署單機部署是將軟件系統所有組件&#xff08;應用、數據庫等&#xff09;部署在單臺服務器上&#xff0c;架構簡單、成本低但存在單點故障風險&#xff0c;適用于低負載或測試場景。一臺服務器壞了&#xff0c;軟件系統無法服務。二、主備&#xff08;冷主備、熱主備…

從體驗到系統工程丨上手評測國內首款 AI 電商 App

作者&#xff1a;王晨&#xff08;望宸&#xff09; 產品界面&#xff0c;往往體現了產品的設計哲學&#xff0c;界面是產品的第一入口。 近期&#xff0c;1688 推出了 1688 AI App&#xff0c;這貌似是國內第一個電商領域的獨立 AI App 應用&#xff08;若不是&#xff0c;歡…

QML QQuickImage: Cannot open: qrc:/images/shrink.png(已解決)

此問題是 在 QT Quick 項目 顯示圖片的時候 遇到&#xff0c;顯示&#xff1a;QML QQuickImage: Cannot open: qrc:/images/shrink.png&#xff0c;不能 打開 圖片。為了解決此問題&#xff0c;找了很多資料&#xff0c;雖然是比較簡單&#xff0c;但對于初學者來說&#xff0c…

maven scope 詳解

Maven 的 scope用于定義依賴項在項目構建生命周期中的可見性和傳遞性&#xff0c;控制依賴在編譯、測試、運行等階段的可用性及是否被打包到最終產物中。以下是詳細解析&#xff1a;?? ??一、Scope 的核心作用????生命周期控制??決定依賴在編譯、測試、運行階段的可用…

Python的一次實際應用:利用Python操作Word文檔的頁碼

Python的一次實際應用&#xff1a;利用Python操作Word文檔的頁碼 需求&#xff1a;一次性處理24個文檔的頁碼。 文檔詳情&#xff1a; 1、每個word文檔包含800頁左右&#xff0c;每一頁包含一個標題和一張圖片。 2、由于圖片有橫排也有豎排&#xff0c;因此&#xff0c;每頁文檔…

Android15 GKI版本分析Kernel Crash問題

環境介紹編譯主機&#xff1a;amd64 Ubuntu 22.04Android源碼&#xff1a;Android15 GKIKernel版本&#xff1a;Linux 6.16Android構建系統&#xff1a;bazel構建工具鏈&#xff1a;gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-定位Linux…

rocky 9部署Zabbix監控

一、rocky安裝 需要注意在設置root用戶密碼時&#xff0c;勾選ssh遠程連接 安裝完成后直接用root登錄 1. 網絡配置 輸入nmtui 進入網絡配置界面 選擇 Edit a connection&#xff0c;再選擇接口 ens3 IPV4更改為Maual 手動模式 根據實際環境配置IP地址 重啟網絡 systemctl …