C++ 條件變量,互斥鎖

C++ 中多線程編程的兩個核心同步原語:互斥鎖 (Mutex)條件變量 (Condition Variable)。它們是實現線程間安全通信和協調的關鍵。

1. 互斥鎖 (Mutex)

核心概念

互斥鎖用于保護共享數據,確保同一時間只有一個線程可以訪問該數據,從而避免數據競爭 (Data Race)

原理:線程在訪問共享數據前先上鎖 (lock),如果鎖已被其他線程占用,則當前線程阻塞 (block) 等待。訪問完成后解鎖 (unlock),讓其他線程有機會獲取鎖。

C++ 中的互斥鎖 (<mutex> 頭文件)

1. std::mutex

最基本的互斥鎖。

cpp

#include <iostream>
#include <thread>
#include <mutex>std::mutex g_mutex; // 全局互斥鎖
int shared_data = 0;void increment() {for (int i = 0; i < 100000; ++i) {g_mutex.lock();    // 上鎖++shared_data;     // 臨界區代碼g_mutex.unlock();  // 解鎖}
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Final value: " << shared_data << std::endl; // 正確輸出 200000return 0;
}
2. std::lock_guard (推薦使用)

RAII 風格的鎖管理,在構造時自動上鎖,析構時自動解鎖,即使發生異常也能保證解鎖,避免死鎖。

cpp

void safe_increment() {for (int i = 0; i < 100000; ++i) {std::lock_guard<std::mutex> lock(g_mutex); // 構造時上鎖,析構時解鎖++shared_data;} // lock_guard 在此析構,自動解鎖
}
3. std::unique_lock (更靈活)

lock_guard 更靈活,可以手動控制上鎖和解鎖的時機,是條件變量必需的伙伴。

cpp

void flexible_increment() {for (int i = 0; i < 100000; ++i) {std::unique_lock<std::mutex> lock(g_mutex); // 自動上鎖++shared_data;// 可以手動提前解鎖,不需要等到作用域結束lock.unlock();// ... 這里可以執行一些不涉及共享數據的操作}
}

2. 條件變量 (Condition Variable)

核心概念

條件變量用于線程間的通信和協調。它允許一個線程等待某個條件成立,而其他線程在條件成立時通知等待的線程。

典型生產者-消費者模式

  • 消費者線程等待"緩沖區不為空"的條件

  • 生產者線程在放入數據后通知消費者

C++ 中的條件變量 (<condition_variable> 頭文件)

基本使用模式

cpp

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;
const int MAX_SIZE = 5;// 生產者線程
void producer() {for (int i = 0; i < 10; ++i) {std::unique_lock<std::mutex> lock(mtx);// 等待條件:隊列未滿// 使用lambda表達式作為等待條件cv.wait(lock, [] { return data_queue.size() < MAX_SIZE; });data_queue.push(i);std::cout << "Produced: " << i << std::endl;lock.unlock(); // 手動解鎖(可選,notify之前解鎖更好)cv.notify_one(); // 通知一個等待的消費者}
}// 消費者線程
void consumer() {while (true) {std::unique_lock<std::mutex> lock(mtx);// 等待條件:隊列不為空cv.wait(lock, [] { return !data_queue.empty(); });int data = data_queue.front();data_queue.pop();std::cout << "Consumed: " << data << std::endl;lock.unlock();cv.notify_one(); // 通知生產者可能有空位了if (data == 9) break; // 收到最后一個數據后退出}
}int main() {std::thread prod(producer);std::thread cons(consumer);prod.join();cons.join();return 0;
}

條件變量的關鍵方法

  1. wait(lock, predicate):

    • 原子地解鎖并阻塞當前線程

    • 被喚醒后重新獲取鎖

    • 檢查 predicate 條件,如果為 false 則繼續等待

  2. notify_one():

    • 喚醒一個等待中的線程(如果有)

  3. notify_all():

    • 喚醒所有等待中的線程


3. 為什么條件變量需要互斥鎖?

條件變量必須與互斥鎖配合使用,原因如下:

  1. 原子性操作:檢查條件和進入等待必須是原子操作,否則可能發生:

    • 線程A檢查條件 → 條件不滿足

    • 線程B修改條件并發出通知

    • 線程A才開始等待 → 通知丟失,線程A永遠等待

  2. 保護共享狀態:條件變量等待的"條件"通常是共享數據,需要用互斥鎖保護。


4. 完整的生產者-消費者示例

cpp

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>class ThreadSafeQueue {
private:std::queue<int> queue_;std::mutex mtx_;std::condition_variable cv_producer_;std::condition_variable cv_consumer_;const int max_size_ = 5;bool stop_ = false;public:void push(int value) {std::unique_lock<std::mutex> lock(mtx_);cv_producer_.wait(lock, [this] { return queue_.size() < max_size_ || stop_; });if (stop_) return;queue_.push(value);std::cout << "Produced: " << value << std::endl;cv_consumer_.notify_one();}int pop() {std::unique_lock<std::mutex> lock(mtx_);cv_consumer_.wait(lock, [this] { return !queue_.empty() || stop_; });if (stop_ && queue_.empty()) return -1;int value = queue_.front();queue_.pop();std::cout << "Consumed: " << value << std::endl;cv_producer_.notify_one();return value;}void stop() {std::lock_guard<std::mutex> lock(mtx_);stop_ = true;cv_producer_.notify_all();cv_consumer_.notify_all();}
};int main() {ThreadSafeQueue queue;std::thread producer([&queue] {for (int i = 0; i < 10; ++i) {queue.push(i);std::this_thread::sleep_for(std::chrono::milliseconds(100));}queue.stop();});std::thread consumer([&queue] {while (true) {int value = queue.pop();if (value == -1) break;std::this_thread::sleep_for(std::chrono::milliseconds(150));}});producer.join();consumer.join();return 0;
}

5. 重要注意事項和最佳實踐

  1. 虛假喚醒 (Spurious Wakeup):線程可能在沒有收到通知的情況下被喚醒,因此必須使用謂詞檢查條件。

  2. 優先使用 std::lock_guardstd::unique_lock:避免手動調用 lock()/unlock()

  3. 在通知前解鎖:在調用 notify_one()notify_all() 前解鎖,可以讓被喚醒的線程立即獲取鎖,提高性能。

  4. 使用 RAII:確保異常安全,所有資源都能正確釋放。

  5. 避免嵌套鎖:容易導致死鎖。

總結對比

特性互斥鎖 (Mutex)條件變量 (Condition Variable)
主要目的保護共享數據,避免數據競爭線程間通信和協調
操作lock(), unlock()wait(), notify_one(), notify_all()
配合使用可以單獨使用必須與互斥鎖配合使用
阻塞原因等待獲取鎖等待某個條件成立
典型模式臨界區保護生產者-消費者

掌握互斥鎖和條件變量是編寫正確、高效多線程 C++ 程序的基礎。

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

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

相關文章

MySQL 8.0 窗口函數詳解:讓數據分析更簡單高效

在日常的數據分析工作中&#xff0c;我們經常需要對數據進行分組排序、計算移動平均值、統計累計求和等操作。在MySQL 8.0之前&#xff0c;這類需求通常需要編寫復雜的子查詢或連接查詢才能實現。而MySQL 8.0引入的窗口函數&#xff08;Window Functions&#xff09;極大地簡化…

【論文閱讀】DeepSeek-LV2:用于高級多模態理解的專家混合視覺語言模型

【論文閱讀】DeepSeek-LV2&#xff1a;用于高級多模態理解的專家混合視覺語言模型 文章目錄【論文閱讀】DeepSeek-LV2&#xff1a;用于高級多模態理解的專家混合視覺語言模型一、介紹二、模型結構三、數據建設**3.1 對齊****3.2 視覺語言預訓練數據****3.3 監督微調數據**四、訓…

一款為開發者而生的開源全棧LLMOps平臺

&#x1f680; 超越ChatGPT&#xff01;一款為開發者而生的全棧LLMOps平臺&#xff1a;LMForge完全指南 作為一名AI應用開發者&#xff0c;你是否也曾遇到過這些令人頭疼的問題&#xff1f; 成本失控&#xff1a;GPT-4的API賬單像雪片一樣飛來&#xff0c;卻不知道錢具體花在…

DeepL Translate在線工具測評:精準翻譯技術文檔與學術論文,支持多格式文檔上傳保留原格式

之前跟你們聊過幫著梳理代碼協作的 GitLens&#xff0c;今天換個偏向文檔翻譯的方向 —— 給你們安利一個在線 AI 翻譯工具「DeepL Translate」&#xff0c;官網地址是DeepL Translate: The worlds most accurate translator&#xff0c;它跟普通翻譯工具不一樣&#xff0c;翻技…

系統配置不是“樂高積木”:制造企業如何通過科學變更管理保障穩定運行

在制造業的數字化進程中&#xff0c;系統配置的穩定性常被忽視。作為一家制造企業的行政經理&#xff0c;我曾親歷這樣的場景&#xff1a;為應對生產波動&#xff0c;各部門頻繁要求調整ERP系統參數&#xff0c;結果導致庫存數據失真、訂單處理延遲&#xff0c;甚至引發客戶投訴…

vscode炒股插件-韭菜盒子AI版

基于vscode插件&#xff0c;原韭菜盒子3.15.0版本開發&#xff0c;新增選股寶快訊功能、AI投資助手、指定股票AI分析功能&#xff08;目前只針對A股&#xff09;&#xff0c;內置AI大模型助手功能&#xff0c;支持ai分析最新資訊、ai分析當日資訊&#xff08;讓ai隨時給你分析股…

Spring Cloud Config 核心原理

Spring Cloud Config 是 Spring Cloud 提供的一個用于集中化管理應用程序各個環境下的配置屬性的解決方案。它支持統一管理配置&#xff0c;并且可以在不重啟應用的情況下動態地更新配置信息&#xff0c;提高開發和運維效率。 主要特點 ? 集中管理配置&#xff1a;可以將不同環…

springboot ioc 控制反轉入門與實戰

Spring Boot3 IOC 項目地址https://gitee.com/supervol/loong-springboot-study&#xff08;記得給個start&#xff0c;感謝&#xff09;IOC 概述在 Spring Boot 3 中&#xff0c;IOC&#xff08;Inversion of Control&#xff0c;控制反轉&#xff09;是核心思想之一&#xff…

LangGraph 重要注意事項和常見問題

01. 數據狀態與歸納函數在前面的課時中&#xff0c;我們說過在 LangGraph 中 節點 在默認情況下返回的字典數據會將原始數據覆蓋&#xff0c;例如下面的代碼最終返回結果是 {"messages": [4]} 而不是 [1,2,3,4]&#xff0c;如下class MyState(TypedDict):messages: l…

避坑指南!解決Navicat運行SQL成功但沒有表的問題

在運行轉儲的SQL文件時&#xff0c;成功運行&#xff0c;試了很多辦法都不顯示出表。原因&#xff1a;當從一個高版本的 MySQL 數據庫導入數據到低版本的 MySQL 數據庫時&#xff0c;可能會遇到兼容性問題。因為高版本的 MySQL 可能支持 utf8mb4_0900_ai_ci&#xff0c;而低版本…

在 Elasticsearch 中使用用戶行為分析:使用 UBI 和 search-ui 創建一個應用程序

作者&#xff1a;來自 Elastic Eduard Martin 及 Alexander Dvila 通過一個實際示例學習如何在 Elasticsearch 中使用 UBI。我們將創建一個在搜索和點擊結果時生成 UBI 事件的應用程序。 想要獲得 Elastic 認證嗎&#xff1f;看看下一次 Elasticsearch Engineer 培訓什么時候開…

SpringBoot3中使用Caffeine緩存組件

SpringBoot3已經把EhCache從框架中刪除了&#xff0c;SpringBoot3默認的緩存組件為Caffeine&#xff0c;那么我們在SpringBoot3中如何去使用它了&#xff1f; 1.添加依賴 <dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>ca…

正則表達式與grep文本過濾詳解

文章目錄前言一、正則表達式概述1.1 定義1.2 主要用途1.3 Linux 中的正則表達式分類1.3.1 基礎正則表達式&#xff08;BRE&#xff09;1.3.2 擴展正則表達式&#xff08;ERE&#xff09;二、正則表達式的基本組成2.1 普通字符2.2 元字符2.2.1 基本元字符2.2.2 重復次數相關2.2.…

Dify 集成 Milvus 配置指南

&#x1f9e9; Dify 集成 Milvus 配置指南 &#x1f527; 詳細配置步驟 1. 環境準備與克隆倉庫 首先確保你的系統已安裝 Git、Docker 和 Docker Compose。然后克隆 Dify 的代碼倉庫&#xff1a; git clone https://github.com/langgenius/dify.git cd dify/docker2. 配置環境變…

為不平,不止于此

口碑可以成就一個人&#xff0c;也可以毀掉一個人&#xff0c; 所以我們選擇用實力去創造兩種無聲的口碑。 要么讓期待的你張口而呼&#xff0c; 要么讓挑剔的你啞口無言。瑪哈特科技創始人 #為不平&#xff0c;不止于此#

0902 C++類的匿名對象

Part 1.梳理思維導圖一.匿名對象1.概念沒有對象名的類對象2.格式類名();3.作用1.給有名對象初始化2.給對象數組初始化3.作為函數的參數傳遞給形參4.例子#include <iostream>using namespace std;class Dog {friend void Dogfriend(Dog &b); private:string name;int …

在 PySpark 中解鎖窗口函數的力量,實現高級數據轉換

本篇文章Mastering PySpark Window Functions: A Practical Guide to Time-Based Analytics適合數據分析和工程師入門了解PySpark的窗口函數。文章的亮點在于詳細介紹了窗口函數的基本概念及其在銷售數據分析中的實際應用&#xff0c;幫助讀者理解如何進行復雜的數據計算而無需…

從理念到實踐:三層解耦架構與“無系統”論

在上一篇中&#xff0c;我們揭示了“五層雙閉環”治理模型如何像骨骼一樣&#xff0c;為數字化轉型提供支撐和定型。但再宏偉的藍圖也需要堅實的施工來實現。今天&#xff0c;我們將深入最具體的實施層面&#xff0c;將“業務重塑”和“以人為本”的理念&#xff0c;轉化為可落…

詳細介紹Linux 內存管理struct page數據結構中的_count和_mapcount有什么區別?

在Linux內核的struct page中&#xff0c;_count&#xff08;或_refcount&#xff09;和_mapcount是兩個關鍵的引用計數成員&#xff0c;它們各自承擔不同的職責。以下是深度解析和代碼案例&#xff1a;1. _count vs _mapcount 區別詳解_count&#xff08;或_refcount&#xff0…

面陣 vs 線陣相機:怎么選不踩坑?選型公式直接套用

面陣vs線陣相機&#xff1a;怎么選不踩坑&#xff1f;選型公式直接套用&#x1f3af;面陣vs線陣相機怎么選不踩坑&#xff1f;&#x1f3af;一、面陣相機&#xff1a;工業檢測的“萬能選手”&#xff0c;拍全圖靠它&#x1f3af;二、線陣相機&#xff1a;大視野/高精度的“專屬…