C++中的condition_variable:條件變量

理解 C++ 中的條件變量(Condition Variable)

在多線程編程中,我們常常需要一個線程等待某個條件的變化,比如等待數據的生成或某個標志位的設置。如果沒有條件變量(condition_variable),線程可能會使用忙等待(不斷檢查條件是否滿足),這會導致 CPU 資源的浪費。條件變量提供了一種高效的等待機制,使線程在等待條件時進入休眠狀態,不占用 CPU 資源,當條件滿足時被喚醒繼續執行。

條件變量的基本概念

條件變量允許一個或多個線程同時阻塞。一般情況下,生產者線程利用支持 std::mutexstd::lock_guardstd::unique_lock 修改共享變量后,并通知條件變量。消費者線程獲取同一個 std::mutex(由 std::unique_lock 持有),并調用 std::condition_variablewaitwait_forwait_untilwait 操作會釋放互斥量,同時掛起該線程。當條件變量收到通知、超時到期或發生虛假喚醒時,線程被喚醒,互斥量也會被原子地重新獲取。如果是虛假喚醒,線程應該檢查條件并繼續等待,以保證業務的正確性。

注意事項

  1. 使用 unique_lock 而不是 lock_guard
    在等待時,管理 mutex 使用的是 unique_lock 而不是 lock_guard,因為等待時是不持有鎖的。wait 函數會調用 mutexunlock 函數,之后再睡眠,直到被喚醒后才持有鎖。lock_guard 沒有 lock/unlock 接口,所以需要用 unique_lock

  2. 偽喚醒
    在對 wait 函數的調用中,條件變量可能會對提供的條件檢查任意多次。這發生在互斥元被鎖定的情況下,并且當測試條件返回 true 時就會立即返回。當等待線程重新獲取互斥元并檢測條件時,如果它并非直接響應另一個線程的通知,這就是所謂的偽喚醒(spurious wake)。偽喚醒的次數和頻率根據定義是不確定的。

  3. 通知丟失
    如果發送方在接收方進入等待狀態之前發送通知,則通知會丟失。

使用場景

  1. 生產者-消費者問題
    生產者線程生成數據并通知消費者線程處理數據。

  2. 任務隊列
    多個線程從一個任務隊列中獲取任務并處理,當隊列為空時,線程進入等待狀態,直到有新的任務被添加。

  3. 事件等待
    一個或多個線程等待某個事件的發生,當事件發生時,喚醒等待的線程進行處理。

std::condition_variablestd::condition_variable_any 的區別

  1. 互斥鎖類型

    • std::condition_variable 只與 std::unique_lock<std::mutex> 類型的鎖配合使用。這意味著它只能與 std::mutex 類型的互斥鎖一起使用。
    • std::condition_variable_any 可以與任何符合基本鎖(BasicLockable)和鎖互換(Lockable)概念的鎖對象配合使用。它不僅可以與 std::mutex 配合,還可以與其他類型的鎖(例如 std::shared_mutex、用戶定義的互斥鎖等)一起使用。
  2. 靈活性

    • std::condition_variable 更加專用,提供了一種高效的實現,因為它只支持 std::unique_lock<std::mutex>
    • std::condition_variable_any 更加通用,提供了更大的靈活性,可以與任何符合要求的鎖一起使用,因此在某些情況下更為便利。

為什么要有 std::condition_variable_any

std::condition_variable_any 的引入是為了提供更大的靈活性,允許開發者使用不同類型的鎖來實現同步需求。在某些情況下,開發者可能需要使用自定義的鎖類型或者 std::shared_mutex 這樣的共享鎖,這時 std::condition_variable 就無法滿足需求,而 std::condition_variable_any 則可以適應這些情況。

代碼示例

以下是一個使用條件變量的代碼示例,演示了如何在 C++ 中使用 std::condition_variable 進行線程同步:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>std::mutex read_mutex_;
std::condition_variable condition_variable_;
bool readFlag_ = false;void do_print_id(int id) {std::unique_lock<std::mutex> uniqueLock(read_mutex_);/*** todo -* 只有當 __pred 條件為 false 時調用 wait() 才會阻塞當前線程,并且在收到其他線程的通知后只有當 __pred 為 true 時才會被解除阻塞* 1. 線程第一次執行到這里時,會執行表達式方法。判斷到 ready 為false。則當前線程阻塞在此,同時解鎖互斥量,不影響其他線程獲取鎖* 2. 當線程被喚醒,首先就是不斷的嘗試重新獲取并加鎖互斥量,若獲取不到鎖就卡在這里反復嘗試加鎖* 3. 若獲取到了鎖,就執行表達式方法,然后繼續往下執行** 函數原型:void condition_variable::wait(unique_lock<mutex>& __lk, _Predicate __pred)*/condition_variable_.wait(uniqueLock, [&] {std::cout << "condition_variable wait.id: " << id << std::endl;return readFlag_;});std::cout << "do_print_id -> thread : " << id << std::endl;
}class QConditionVariable {
public:void task1() {std::thread threads[10];for (int i = 0; i < 10; ++i) {threads[i] = std::thread(do_print_id, i);}for (std::thread &item : threads) {item.detach();}std::this_thread::sleep_for(std::chrono::seconds(2));notify();std::this_thread::sleep_for(std::chrono::seconds(5));std::cout << "task1--end" << std::endl;}void task2() {std::unique_lock<std::mutex> uniqueLock(read_mutex_);std::cv_status cvStatus = condition_variable_.wait_for(uniqueLock, std::chrono::seconds(5));if (cvStatus == std::cv_status::no_timeout) {//表示條件變量等待成功(條件滿足或被通知)。} else if (cvStatus == std::cv_status::timeout) {//表示條件變量等待超時。}}void task3() {std::unique_lock<std::mutex> uniqueLock(read_mutex_);/*** wait_for: 等待特定的時間段,直到被通知或時間到期。* 執行到這里時,當前線程將進入等待狀態,等待最多1秒鐘:*  如果在這1秒鐘內,條件變量 condition_variable_ 被其他線程通知(通常通過 notify_one 或 notify_all),wait_for 會立即返回 std::cv_status::no_timeout,并且 while 循環會終止。*  如果這1秒鐘內沒有收到通知,wait_for 返回 std::cv_status::timeout,循環條件為真,線程繼續執行循環體內的代碼(這里是空的,沒有其他操作),然后再次進入等待。*/while (condition_variable_.wait_for(uniqueLock, std::chrono::seconds(1)) == std::cv_status::timeout) {}}private:void notify() {std::cout << "start----notify" << std::endl;std::unique_lock<std::mutex> uniqueLockNotify(read_mutex_);readFlag_ = true;condition_variable_.notify_all();std::cout << "end----notify" << std::endl;}
};

在這個示例中,我們創建了一個簡單的類 QConditionVariable,包含了兩個任務 task1task2task1 生成 10 個線程,每個線程都會調用 do_print_id 函數,等待條件變量 condition_variable_ 的通知。task2 則是一個等待操作示例,等待特定時間段或直到被通知。

通過條件變量,我們可以有效地管理多線程程序中的同步和通信,避免忙等待,提高程序的執行效率。

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

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

相關文章

啟智暢想火車類集裝箱號碼識別技術,軟硬件解決方案

集裝箱號碼識別需求&#xff1a; 實時檢測車皮號、火車底盤號碼、集裝箱號碼&#xff0c;根據火車類型分為以下三種情況&#xff1a; 1、純車皮&#xff0c;只檢測車皮號&#xff1b; 2、火車拉貨箱&#xff08;半車皮&#xff09;&#xff0c;檢測車皮號集裝箱號碼&#xff1b…

如何從0搭建一個Ai智體day01

&#x1f4da;《AI破局行動&#xff5c;AI智能體&#xff08;coze&#xff09;實戰手冊》&#xff1a; https://d16rg8unadx.feishu.cn/wiki/XQESwHW5HiPFlrkZbkqc0Xp7nEb 說明 這個是授權訪問的&#xff0c;想學習加我 微信/ Github:** watchpoints &#x1f4fa;Day1-大圣直播…

玩轉HarmonyOS NEXT之常用布局三

輪播&#xff08;Swiper&#xff09; Swiper組件提供滑動輪播顯示的能力。Swiper本身是一個容器組件&#xff0c;當設置了多個子組件后&#xff0c;可以對這些子組件進行輪播顯示。通常&#xff0c;在一些應用首頁顯示推薦的內容時&#xff0c;需要用到輪播顯示的能力。 針對…

git開發流程

分支介紹 master - 主分支 所有提供給用戶使用的正式版本&#xff0c;都在這個主分支上發布 開發者在此分支 不可進行 push 操作 dev - 開發分支 日常開發所使用的分支&#xff0c;開發者完成的階段性功能模塊將首先被合并到此分支 此分支亦是團隊內部測試、階段性工作驗證…

Xcode 16 beta3 真機調試找不到 Apple Watch 的嘗試解決

很多小伙伴們想用 Xcode 在 Apple Watch 真機上調試運行 App 時卻發現&#xff1a;在 Xcode 設備管理器中壓根找不到對應的 Apple Watch 設備。 大家是否已將 Apple Watch 和 Mac 都重啟一萬多遍了&#xff0c;還是束手無策。 Apple Watch not showing in XCodeApple Watch wo…

C++基礎語法:STL之容器(1)--容器概述和序列概述

前言 "打牢基礎,萬事不愁" .C的基礎語法的學習 引入 STL是標準模板庫,類模板主要是用來做容器的,所以個人理解:標準模板庫是"標準容器庫".容器是STL的核心 .以<C Prime Plus> 6th Edition(以下稱"本書")內容理解容器. 類模板內容回顧 類…

NineData全面支持PostgreSQL可視化表結構設計

“PostgreSQL 是最像 Oracle 的開源關系型數據庫“&#xff0c;也正因為如此&#xff0c;很多企業都青睞 PostgreSQL&#xff0c;拿它當成 Oracle 的替代品。所以毫無疑問&#xff0c;目前 PostgreSQL 在企業中非常常見。 對于直接接觸 PostgreSQL 的開發人員而言&#xff0c;…

echarts多柱堆疊的X軸順序

在一些圖表場景中&#xff0c;需要顯示多柱堆疊的數據&#xff0c;那么X軸上每一段單位區域內會有多根柱子&#xff0c;每一根柱子標識不同的數量項含義&#xff0c;那么怎樣控制這幾根柱的左右順序呢&#xff1f; 其實這跟echarts的option里的series由關&#xff0c;開始我以為…

快速排序及歸并排序的實現與排序的穩定性

目錄 快速排序 一. 快速排序遞歸的實現方法 1. 左右指針法 步驟思路 為什么要讓end先走&#xff1f; 2. 挖坑法 步驟思路 3. 前后指針法 步驟思路 二. 快速排序的時間和空間復雜度 1. 時間復雜度 2. 空間復雜度 三. 快速排序的優化方法 1. 三數取中優化 2. 小區…

實驗豐富、原創改進!|多策略改進蜣螂優化算法(MATLAB)

本文內容來源于本人公眾號&#xff1a;KAU的云實驗臺&#xff0c;更新內容&#xff1a;智能優化算法及其改進應用。 本文核心內容&#xff1a; 新穎的多策略改進蜣螂優化算法 對比算法包括&#xff1a;高引用/新發布/經典/其他DBO變體&#xff08;共11種&#xff09; 實驗設計…

用c語言寫一個貪吃蛇游戲

貪吃蛇游戲通常涉及到終端圖形編程和簡單的游戲邏輯。以下是一個基本的實現示例&#xff0c;包括貪吃蛇的移動、食物生成、碰撞檢測等功能。 1. 貪吃蛇游戲的基本結構 貪吃蛇游戲可以分為以下幾個部分&#xff1a; 游戲地圖和終端繪制&#xff1a;使用二維數組表示游戲地圖&am…

SpringBoot結合ip2region實現博客評論顯示IP屬地

你好呀&#xff0c;我是小鄒。 在現代的Web應用中&#xff0c;特別是博客和論壇類網站&#xff0c;為用戶提供地理定位服務&#xff08;如顯示用戶所在地理位置&#xff09;可以極大地增強用戶體驗。本文將詳細探討如何使用Java和相關技術棧來實現在博客評論中顯示用戶的地址信…

Java實驗3

實驗內容 學生信息管理系統 學生成績表Student(Sno 字符串&#xff0c;長度9, Sname 字符串&#xff0c;長度10, Class 字符串&#xff0c;長度10, Age 整型, Sex 字符串&#xff0c;長度2) 實現如下功能&#xff1a; A&#xff0e;輸入若干個學生的信息到Student表&#x…

初學Python必須知道的14個強大單行代碼

引言&#xff1a;Python的魅力與單行代碼的重要性 Python以其簡潔明了的語法、豐富的內置函數和強大的第三方庫深受廣大開發者喜愛。尤其對于編程小白來說&#xff0c;學習Python就像打開了一扇通向編程世界的大門。而單行代碼&#xff0c;作為Python魅力的一部分&#xff0c;…

【NetTopologySuite類庫】合并所有幾何的包圍盒AABB

流程示意圖 示例代碼 using GeoAPI.Geometries; using Microsoft.VisualStudio.TestTools.UnitTesting; using NetTopologySuite.Geometries; using NetTopologySuite.IO; using System.Collections.Generic; using System.Linq;namespace Test472 {[TestClass]public class T…

深度解析:電商訂單API及其技術實現

隨著電子商務的發展&#xff0c;實體企業開拓電商渠道的越來越多&#xff0c;原有的管理系統都需要增加電商業務管理功能&#xff0c;其中&#xff0c;對電商訂單的管理是每一個電商商家都需要的功能&#xff0c;所以對于開發者來說&#xff0c;了解電商API是什么是非常重要的&…

第100+16步 ChatGPT學習:R實現Xgboost分類

基于R 4.2.2版本演示 一、寫在前面 有不少大佬問做機器學習分類能不能用R語言&#xff0c;不想學Python咯。 答曰&#xff1a;可&#xff01;用GPT或者Kimi轉一下就得了唄。 加上最近也沒啥內容寫了&#xff0c;就幫各位搬運一下吧。 二、R代碼實現Xgboost分類 &#xff08…

LeetCode題練習與總結:比較版本號--165

一、題目描述 給你兩個 版本號字符串 version1 和 version2 &#xff0c;請你比較它們。版本號由被點 . 分開的修訂號組成。修訂號的值 是它 轉換為整數 并忽略前導零。 比較版本號時&#xff0c;請按 從左到右的順序 依次比較它們的修訂號。如果其中一個版本字符串的修訂號較…

C++動態內存的管理

今天來分享C動態內存管理相關知識&#xff0c;閑言勿談&#xff0c;直接上干貨。 1. 動態內存的開辟和銷毀(new和delete) (1)前置知識&#xff1a;我們知道c語言有malloc和calloc和realloc三個函數可以進行動態的開辟內存&#xff0c;那么它們有什么區別呢&#xff1f;首先是…

MPS 后端

本文來自&#xff1a; https://pytorch.org/docs/stable/notes/mps.html https://pytorch.ac.cn/docs/stable/notes/mps.html MPS 后端 mps 設備支持 在使用 Metal 編程框架的 MacOS 設備上&#xff0c;進行高性能 GPU 訓練。 它引入了新的設備&#xff0c;將機器學習計算圖和…