『 C++ 』多線程同步:條件變量及其接口的應用實踐

文章目錄

  • 條件變量概述
    • 條件變量簡介
    • 條件變量的基本用法
  • 案例:兩個線程交替打印奇偶數
    • 代碼解釋
  • `std::unique_lock::try_lock_until` 介紹
    • 代碼示例
    • 代碼解釋
    • 注意事項
  • `std::condition_variable::wait` 詳細解析與示例
    • `std::condition_variable::wait` 接口介紹
    • 代碼示例
    • 代碼解釋
    • 關于最后一個互斥鎖可能再次阻塞線程
  • 條件變量其他接口的應用示例
    • `wait_for` 接口應用
    • `wait_until` 接口應用
    • `notify_one` 接口應用
    • `notify_all` 接口應用

條件變量概述

在 C++ 多線程編程里,同步機制是極為關鍵的部分,它能保證多個線程安全且高效地訪問共享資源。其中,條件變量(std::condition_variable)和 std::unique_lock::try_lock_until 是很實用的工具。接下來,我們會深入探討它們的應用。

條件變量簡介

條件變量是 C++ 標準庫中的一個同步原語,它可讓線程在特定條件達成時被喚醒。其主要用途是線程間的等待 - 通知機制,一個線程等待某個條件成立,而另一個線程在條件成立時通知等待的線程。

條件變量的基本用法

條件變量的基本操作包含 waitwait_forwait_untilnotify_one/notify_all

  • wait:使線程進入等待狀態,同時釋放互斥量,直到被其他線程喚醒。
  • wait_for:線程會等待一段時間,若在這段時間內被通知則繼續執行,若超時則繼續執行。
  • wait_until:線程會等待到指定的時間點,若在該時間點前被通知則繼續執行,若到達時間點還未被通知則繼續執行。
  • notify_one:喚醒一個正在等待的線程。
  • notify_all:喚醒所有正在等待的線程。

案例:兩個線程交替打印奇偶數

以下是一個使用條件變量實現兩個線程交替打印奇偶數的案例:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>using namespace std;
void two_thread_print()
{std::mutex mtx;condition_variable c;int n = 100;bool flag = true;thread t1([&]() {int i = 0;while (i < n){unique_lock<mutex> lock(mtx);c.wait(lock, [&]()->bool {return flag; });cout << i << endl;flag = false;i += 2; // 偶數c.notify_one();}});thread t2([&]() {int j = 1;while (j < n){unique_lock<mutex> lock(mtx);c.wait(lock, [&]()->bool {return !flag; });cout << j << endl;flag = true;j += 2; // 奇數c.notify_one();}});t1.join();t2.join();
}int main()
{two_thread_print();return 0;
}    

代碼解釋

  1. 線程函數
    • t1 線程負責打印偶數,它會等待 flagtrue 時開始打印,打印完成后將 flag 置為 false,并通知另一個線程。
    • t2 線程負責打印奇數,它會等待 flagfalse 時開始打印,打印完成后將 flag 置為 true,并通知另一個線程。
  2. 條件變量的使用
    • c.wait(lock, [&]()->bool {return flag; });c.wait(lock, [&]()->bool {return !flag; }); 用于線程的等待,當條件不滿足時線程會進入等待狀態,同時釋放互斥量。
    • c.notify_one(); 用于喚醒另一個等待的線程。
  3. 線程同步
    • 通過 t1.join()t2.join() 確保主線程等待兩個子線程執行完畢。

std::unique_lock::try_lock_until 介紹

std::unique_lock::try_lock_until 是 C++ 標準庫 <mutex> 頭文件中的一部分,用于嘗試在指定的時間點之前鎖定關聯的互斥量。如果在指定時間之前成功鎖定,它會返回 true;若超時仍未鎖定,則返回 false

代碼示例

下面的代碼模擬了一個多線程場景,主線程會嘗試在指定時間內鎖定互斥量,而工作線程會先占用一段時間互斥量。

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>std::mutex mtx;void worker() {std::this_thread::sleep_for(std::chrono::seconds(2));std::lock_guard<std::mutex> lock(mtx);std::cout << "Worker thread locked the mutex." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(2));std::cout << "Worker thread unlocked the mutex." << std::endl;
}int main() {std::thread t(worker);std::unique_lock<std::mutex> lock(mtx, std::defer_lock);auto timeout = std::chrono::steady_clock::now() + std::chrono::seconds(1);if (lock.try_lock_until(timeout)) {std::cout << "Main thread locked the mutex." << std::endl;lock.unlock();} else {std::cout << "Main thread failed to lock the mutex within the timeout." << std::endl;}t.join();return 0;
}    

代碼解釋

  1. 線程函數 worker:工作線程會先休眠 2 秒,然后鎖定互斥量 mtx,打印信息,再休眠 2 秒,最后解鎖互斥量。
  2. 主線程邏輯
    • 創建工作線程 t
    • 創建 std::unique_lock 對象 lock,初始時不鎖定互斥量。
    • 設定超時時間為當前時間加上 1 秒。
    • 調用 try_lock_until 嘗試在超時時間之前鎖定互斥量。
    • 根據返回結果輸出相應信息。
  3. 線程同步:主線程通過 t.join() 等待工作線程結束。

注意事項

  • 此函數適用于需要在一定時間內嘗試鎖定互斥量的場景,避免無限期等待。
  • 超時時間點可以使用不同的時鐘類型和時間單位,如 std::chrono::steady_clockstd::chrono::system_clock

std::condition_variable::wait 詳細解析與示例

std::condition_variable::wait 接口介紹

std::condition_variable::wait 有兩種重載形式:

// 無條件形式
void wait (unique_lock<mutex>& lck);
// 帶謂詞形式
template <class Predicate>  
void wait (unique_lock<mutex>& lck, Predicate pred);

當前線程(應已鎖定互斥鎖)的執行將被阻止,直到收到通知。在阻塞線程的那一刻,該函數會自動調用 lck.unlock(),允許其他鎖定的線程繼續。一旦收到通知(顯式地,由其他線程通知),該函數就會解除阻塞并調用 lck.lock(),離開時的狀態與調用函數時的狀態相同。然后函數返回(請注意,最后一個互斥鎖可能會在返回之前再次阻塞線程)。

通常,通過在另一個線程中調用 notify_onenotify_all 來通知函數喚醒。但某些實現可能會在不調用這些函數的情況下產生虛假的喚醒調用。因此,此功能的用戶應確保滿足其恢復條件。

如果指定了帶謂詞的版本(2),則該函數僅在 pred() 返回 false 時阻塞,并且通知只能在線程變為 pred() 返回 true 時取消阻塞線程(這對于檢查虛假喚醒調用特別有用)。

代碼示例

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;
bool ready = false;void worker() {std::unique_lock<std::mutex> lock(mtx);// 使用帶謂詞的 wait 版本cv.wait(lock, []{ return ready; });std::cout << "Worker thread is working." << std::endl;
}int main() {std::thread t(worker);{std::lock_guard<std::mutex> lock(mtx);ready = true;}cv.notify_one();t.join();return 0;
}

代碼解釋

  1. worker 線程worker 線程獲取 unique_lock 并調用 cv.wait(lock, []{ return ready; });,如果 readyfalse,線程會進入等待狀態,同時釋放互斥鎖。當收到通知并且 ready 變為 true 時,線程會重新獲取互斥鎖并繼續執行。
  2. 主線程:主線程設置 readytrue 并調用 cv.notify_one() 通知等待的線程。

關于最后一個互斥鎖可能再次阻塞線程

cv.wait(lock, []{ return ready; }); 中,當收到通知且謂詞 []{ return ready; } 返回 true 時,wait 函數會嘗試重新鎖定互斥鎖(調用 lck.lock())。如果此時互斥鎖被其他線程持有,當前線程會再次被阻塞,直到成功獲取互斥鎖。

條件變量其他接口的應用示例

wait_for 接口應用

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>std::mutex mtx;
std::condition_variable cv;
bool ready = false;void worker() {std::unique_lock<std::mutex> lock(mtx);if (cv.wait_for(lock, std::chrono::seconds(2), []{ return ready; })) {std::cout << "Worker thread got notified in time." << std::endl;} else {std::cout << "Worker thread timed out." << std::endl;}
}int main() {std::thread t(worker);std::this_thread::sleep_for(std::chrono::seconds(3));{std::lock_guard<std::mutex> lock(mtx);ready = true;}cv.notify_one();t.join();return 0;
}

這里 worker 線程會等待 2 秒,如果在這 2 秒內沒有收到通知,就會超時繼續執行。

wait_until 接口應用

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>std::timed_mutex mtx;
bool ready = false;void worker() {std::this_thread::sleep_for(std::chrono::seconds(2));std::lock_guard<std::timed_mutex> lock(mtx); // 使用 std::timed_mutexstd::cout << "Worker thread locked the mutex." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(2));ready = true;std::cout << "Worker thread unlocked the mutex." << std::endl;
}int main() {std::thread t(worker);std::unique_lock<std::timed_mutex> lock(mtx, std::defer_lock); // 使用 std::timed_mutexauto timeout = std::chrono::system_clock::now() + std::chrono::seconds(1);if (lock.try_lock_until(timeout)) { // 正確:std::timed_mutex 支持 try_lock_untilstd::cout << "Main thread locked the mutex." << std::endl;lock.unlock();}else {std::cout << "Main thread failed to lock the mutex within the timeout." << std::endl;}t.join();return 0;
}

worker 線程會等待到指定的時間點,如果在該時間點前收到通知則繼續執行,否則超時繼續執行。

notify_one 接口應用

參考前面兩個線程交替打印奇偶數的例子,c.notify_one(); 用于喚醒另一個等待的線程。

notify_all 接口應用

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>std::mutex mtx;
std::condition_variable cv;
bool ready = false;void worker(int id) {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, []{ return ready; });std::cout << "Worker thread " << id << " is working." << std::endl;
}int main() {std::vector<std::thread> threads;for (int i = 0; i < 3; ++i) {threads.emplace_back(worker, i);}{std::lock_guard<std::mutex> lock(mtx);ready = true;}cv.notify_all();for (auto& t : threads) {t.join();}return 0;
}

在這個例子中,notify_all 會喚醒所有等待的線程。

通過條件變量和 std::unique_lock::try_lock_until,我們能夠更靈活地實現多線程同步,避免死鎖和資源競爭問題。在實際開發中,根據具體需求合理運用這些工具,可以提高程序的性能和穩定性。

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

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

相關文章

【VolView】純前端實現CT三維重建-CBCT

文章目錄 什么是CBCTCBCT技術路線使用第三方工具使用Python實現使用前端實現 純前端實現方案優缺點使用VolView實現CBCT VolView的使用1.克隆代碼2.配置依賴3.運行4.效果 進階&#xff1a;VolView配合Python解決卡頓1.修改VtkThreeView.vue2.新增Custom3DView.vue3.Python生成s…

debug - 安裝.msi時,為所有用戶安裝程序

文章目錄 debug - 安裝.msi時&#xff0c;為所有用戶安裝程序概述筆記試試在目標.msi后面直接加參數的測試 備注備注END debug - 安裝.msi時&#xff0c;為所有用戶安裝程序 概述 為了測試&#xff0c;裝了一個test.msi. 安裝時&#xff0c;只有安裝路徑的選擇&#xff0c;沒…

Java Stream兩種list判斷字符串是否存在方案

這里寫自定義目錄標題 背景初始化方法一、filter過濾方法二、anyMatch匹配 背景 在項目開發中&#xff0c;經常遇到篩選list中是否包含某個子字符串&#xff0c;有多種方式&#xff0c;本篇主要介紹stream流的filter和anyMatch兩種方案&#xff0c;記錄下來&#xff0c;方便備…

DeepSeek vs 通義大模型:誰將主導中國AI的未來戰場?

當你在深夜調試代碼時,是否幻想過AI伙伴能真正理解你的需求?當企業面對海量數據時,是否期待一個真正智能的決策大腦? 這場由DeepSeek和通義領銜的大模型之爭,正在重塑中國AI產業的競爭格局。本文將為你揭開兩大技術巨頭的終極對決! 一、顛覆認知的技術突破 1.1 改變游戲…

3. 軸指令(omron 機器自動化控制器)——>MC_SetOverride

機器自動化控制器——第三章 軸指令 12 MC_SetOverride變量?輸入變量?輸出變量?輸入輸出變量 功能說明?時序圖?重啟運動指令?多重啟動運動指令?異常 MC_SetOverride 變更軸的目標速度。 指令名稱FB/FUN圖形表現ST表現MC_SetOverride超調值設定FBMC_SetOverride_instan…

從像素到世界:自動駕駛視覺感知的坐標變換體系

接著上一篇 如何讓自動駕駛汽車“看清”世界?坐標映射與數據融合詳解的概述,這一篇詳細講解自動駕駛多目視覺系統設計原理,并給出應用示例。 摘要 在自動駕駛系統中,準確的環境感知是實現路徑規劃與決策控制的基礎。本文系統性地解析圖像坐標系、像素坐標系、相機坐標系與…

附錄B ISO15118-20測試命令

本章節給出ISO15118-20協議集的V2G命令&#xff0c;包含json、xml&#xff0c;并且根據exiCodec.jar編碼得到exi內容&#xff0c; 讀者可以參考使用&#xff0c;測試編解碼庫是否能正確編解碼。 B.1 supportedAppProtocolReq json: {"supportedAppProtocolReq": {…

VLAN章節學習

為什么會有vlan這個技術&#xff1f; 1.通過劃分廣播域來降低廣播風暴導致的設備性能下降&#xff1b; 2.提高網絡管理的靈活性和通過隔離網絡帶來的安全性&#xff1b; 3.在成本不變的情況下增加更多的功能性&#xff1b; VLAN又稱虛擬局域網&#xff08;再此擴展&#xf…

FPGA時鐘約束

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 目錄 前言 一、Create_clock 前言 時鐘周期約束&#xff0c;就是對時鐘進行約束。 一、Create_clock create_clock -name <name> -period <period> -waveform …

機房布局和布線的最佳實踐:如何打造高效、安全的機房環境

機房布局和布線的最佳實踐:如何打造高效、安全的機房環境 大家好,我是Echo_Wish。今天我們來聊聊機房布局和布線的問題,這可是數據中心和IT運維中的一個非常重要的環節。不管是剛剛接觸運維的新人,還是已經摸爬滾打多年的老兵,都應該對機房的布局和布線有一個清晰的認識。…

spring-security原理與應用系列:建造者

目錄 1.構建過程 AbstractSecurityBuilder AbstractConfiguredSecurityBuilder WebSecurity 2.建造者類圖 SecurityBuilder ???????AbstractSecurityBuilder ???????AbstractConfiguredSecurityBuilder ???????WebSecurity 3.小結 緊接上一篇文…

OpenHarmony子系統開發 - 電池管理(二)

OpenHarmony子系統開發 - 電池管理&#xff08;二&#xff09; 五、充電限流限壓定制開發指導 概述 簡介 OpenHarmony默認提供了充電限流限壓的特性。在對終端設備進行充電時&#xff0c;由于環境影響&#xff0c;可能會導致電池溫度過高&#xff0c;因此需要對充電電流或電…

xy軸不等比縮放問題——AUTOCAD c#二次開發

在 AutoCAD .net api里&#xff0c;部分實體&#xff0c;像文字、屬性、插入塊等&#xff0c;是不支持非等比縮放的。 如需對AutoCAD中圖形進行xyz方向不等比縮放&#xff0c;則需進行額外的函數封裝。 選擇圖元&#xff0c;指定縮放基準點&#xff0c;scaleX 0.5, scaleY …

如何在 HTML 中創建一個有序列表和無序列表,它們的語義有何不同?

大白話如何在 HTML 中創建一個有序列表和無序列表&#xff0c;它們的語義有何不同&#xff1f; 1. HTML 中有序列表和無序列表的基本概念 在 HTML 里&#xff0c;列表是一種用來組織信息的方式。有序列表就是帶有編號的列表&#xff0c;它可以讓內容按照一定的順序呈現&#…

kafka的文章

1.面試的問題 要點 至多一次、恰好一次數據一致性超時重試、冪等消息順序消息擠壓延時消息 1.1 kafaka 生產消息的過程。 在消息發送的過程中&#xff0c;涉及到了兩個線程&#xff0c;一個是main 線程&#xff0c;一個是sender 線程。在main 線程中創建了一個雙端隊列 Reco…

以mysql 為例,增刪改查語法及其他高級特性

以下是 MySQL 的 增刪改查語法及 高級特性的詳細整理&#xff0c;結合示例說明&#xff1a; 1. 基礎操作&#xff08;CRUD&#xff09; (1) 創建數據&#xff08;INSERT&#xff09; -- 單條插入 INSERT INTO users (id, name, email) VALUES (1, Alice, aliceexample.com);…

Postman最新詳細安裝及使用教程【附安裝包】

一、Postman介紹 ?Postman是一個功能強大的API測試工具&#xff0c;主要用于模擬和測試各種HTTP請求&#xff0c;支持GET、POST、PUT、DELETE等多種請求方法。?通過Postman&#xff0c;用戶可以發送請求并查看返回的響應&#xff0c;檢查響應的內容和狀態&#xff0c;從而驗…

第十三章 : Names in Templates_《C++ Templates》notes

Names in Templates 重難點多選題設計題 重難點 1. 名稱分類與基本概念 知識點&#xff1a; 限定名&#xff08;Qualified Name&#xff09;&#xff1a;使用::或.顯式指定作用域的名稱&#xff08;如std::vector&#xff09;非限定名&#xff08;Unqualified Name&#xff0…

整合vue+Element UI 開發管理系統

1、 安裝 Node.js 和 npm 確保安裝了 Node.js 和 npm。可以通過 Node.js 官網 下載。 2、 創建 Vue 項目 安裝cli npm install -g vue/cli 使用 Vue CLI 創建一個新的 Vue 項目。 vue create admin-system cd admin-system npm run serve 出現這個頁面表示vue創建成功 安…

3. 軸指令(omron 機器自動化控制器)——>MC_Stop

機器自動化控制器——第三章 軸指令 9 MC_Stop變量?輸入變量?輸出變量?輸入輸出變量 功能說明?指令詳情?時序圖?重啟運動指令?多重啟動運動指令?異常 MC_Stop 使軸減速停止。 指令名稱FB/FUN圖形表現ST表現MC_Stop強制停止FBMC_Stop_instance (Axis :《參數》 ,Execu…