C++11標準庫 條件變數 <condition_variable> 梳理

来源:https://www.cnblogs.com/DSCL-ing/p/18302142
-Advertisement-
Play Games

目錄<condition_variable>condition_variable類類方法生產者消費者模型 -- 阻塞隊列單條件變數版condition_variable_any模板類區別優缺點 <condition_variable> 條件變數是C++11提供的另外一種用於等待的同步機制,它能阻塞一 ...


目錄

<condition_variable>

條件變數是C++11提供的另外一種用於等待的同步機制,它能阻塞一個或多個線程,直到收到另外一個線程發出的通知或者超時時,才會喚醒當前阻塞的線程。條件變數需要和互斥量配合起來使用,C++11提供了兩種條件變數:

條件變數為什麼叫變數?

在電腦科學和併發編程中,條件變數是一種用於線程同步的機制,它們之所以被稱為“變數”,主要有以下幾個原因:

  1. 狀態表示:條件變數本質上是一個表示狀態的對象,這個狀態可以被其他線程檢查和修改。變數這個詞意味著它是一個可以存儲和表示某種狀態的實體。
  2. 動態性:條件變數的狀態是動態變化的。線程可以等待一個條件變數的某個狀態,然後在條件滿足時被喚醒。這種動態變化的特性使得它像一個普通的變數,可以在程式運行時不斷變化。
  3. 操作性:條件變數可以通過特定的操作來改變其狀態。通常,條件變數有兩個主要操作:等待(wait)和通知(signal或broadcast)。這些操作類似於對普通變數進行的讀寫操作,只不過這些操作影響的是線程的執行流。
  4. 命名約定:在許多編程語言和庫中,條件變數被設計為一種數據結構或對象,並且通常以變數的形式存在於代碼中。為了與其他同步機制(如互斥鎖、信號量等)區分開來,並保持命名的一致性,使用“變數”這個詞來描述它們。

綜上所述,條件變數被稱為“變數”主要是因為它們具有狀態表示的特性,可以通過操作改變狀態,併在程式中以變數的形式出現,從而幫助線程實現同步。

condition_variable類

condition_variable實現在<condition_variable>中,在VS2019中內有聲明,但是GCC沒有.

類方法

  1. 線程等待(阻塞/休眠)函數
1. void wait (unique_lock<mutex>& lck); //,解鎖,進入休眠,等待喚醒
2. template <class Predicate>
void wait (unique_lock<mutex>& lck, Predicate pred);

如果線程被該函數阻塞,這個線程會釋放占有的互斥鎖的所有權,當阻塞解除之後這個線程會重新得到互斥鎖的所有權,繼續向下執行

  • condition_variable的wait()的lck參數無法直接使用互斥鎖,必須搭配std::unique_lock<>類使用

  • wait的第二重載方法中,Pred參數是一個模板參數,用於接收返回值為bool函數或函數對象/lambda表達式.

    每次喚醒在wait隊列內休眠的線程時,線程都會檢查Rred的值,只有為真時才會繼續往下執行,否則繼續休眠

    • Pred值為假,線程進入休眠,等待喚醒
    • Pred值為真,線程繼續向下執行.
  1. 線程通知/喚醒函數 -- (notify:通知)
1. void notify_one() noexcept; //在wait隊列中喚醒一個
2. void notify_all() noexcept; //全部喚醒
  1. wait_for和wait_until
    wait_for()函數,waitr_until()函數都和wait()的功能是一樣的,只不過多了一個阻塞時長,假設阻塞的線程沒有被其他線程喚醒,當阻塞時長用完之後,線程就會自動解除阻塞,繼續向下執行。
wait_for
a.
	template <class Clock, class Duration>
	cv_status wait_until (unique_lock<mutex>& lck,
	                      const chrono::time_point<Clock,Duration>& abs_time);
b.
	template <class Clock, class Duration, class Predicate>
	bool wait_until (unique_lock<mutex>& lck,
	                 const chrono::time_point<Clock,Duration>& abs_time, Predicate pred);
                 
wait_until
a.
	template <class Clock, class Duration>
	cv_status wait_until (unique_lock<mutex>& lck,
	                      const chrono::time_point<Clock,Duration>& abs_time);
b.
	template <class Clock, class Duration, class Predicate>
	bool wait_until (unique_lock<mutex>& lck,
	                 const chrono::time_point<Clock,Duration>& abs_time, Predicate pred);

生產者消費者模型 -- 阻塞隊列

單條件變數版

運用:wait(lck), wait(lck,Pred), notify_all(), condition_variable

#include<iostream> //std::cout 
#include<thread> //std::thread
#include<mutex> //std::mutex, std::unique_lock, std::lock_guard
#include<queue> //std::queue
#include<condition_variable> //std::condition_variable
#include<functional> //std::bind

//設計概要
/*
定長隊列+自動管理增刪
同步 == 獨占互斥鎖
- 生產者生產 == 增加 -- bool?生產成功返回true,放不下false --- (生產者一定知道自己已生產)生產:通知消費者
- 消費者消費 == 刪除 -- bool?消費成功返回true,沒有了false --- (同理)       消費:通知生產者
*/

//單條件變數版 --  簡化邏輯
template<class T>
class  BlockQueue {

public:
    bool isEmpty() {
        return _bqueue.empty();
    }
    bool isFull() {
        return _bqueue.size() == _capacity;
    }
    //AddTask
    void Push(const T& t) {
        std::unique_lock<std::mutex> lck(_mtx);
        while (isFull()) {
            cv.wait(lck);
        }
        _bqueue.push(t);
        std::cout << "模擬插入數據..." << t << "\n";
        lck.unlock();
        cv.notify_all();
    }
    //DelTask
    void Pop() {
        std::unique_lock<std::mutex>lck(_mtx);
        cv.wait(lck, [this]() {
            bool flag = isEmpty(); //為空時休眠
            return !flag; //增加可讀性的策略
            });
        T t = _bqueue.front();
        _bqueue.pop();
        std::cout << "模擬處理數據..." << t << "\n";
        lck.unlock();
        cv.notify_all();
    }

private:
    std::queue<T> _bqueue;
    std::mutex _mtx;
    std::condition_variable cv;  //必須搭配all
    size_t _capacity = 5; //隊列定長大小
    
    //std::condition_variable _not_full;  //非滿時喚醒生產者
    //std::condition_variable _not_empty; //非空時喚醒消費者
};


int main() {
    BlockQueue<int> tq;
    auto produce = std::bind(&BlockQueue<int>::Push, &tq, std::placeholders::_1);
    auto consume = std::bind(&BlockQueue<int>::Pop, &tq);
    std::thread t1[5];
    std::thread t2[5];
    for (int i = 0; i < 5; i++) {
        t1[i] = std::thread(produce, i);
        t2[i] = std::thread(consume);
    }

    for (int i = 0; i < 5; i++) {
        t1[i].join();
        t2[i].join();
    }

    return 0;
}

condition_variable_any模板類

condition_variable_any實現在<condition_variable>中,在VS2019中內有聲明,但是GCC沒有.

區別

condition_variable_any與condition_variable的區別是

  • condition_variable_any可以直接給wait()函數傳互斥鎖std::mutex、std::timed_mutex、std::recursive_mutex、std::recursive_timed_mutex四種.
  • condition_variable在wait()時必須搭配unique_lock使用

優缺點

  • condition_variable實現簡單,效率更高.缺點是只能搭配std::mutex和unique_lock使用
  • condition_variable_any更加靈活,但是實現複雜,效率會低一些,僅僅是低一些,對於現代電腦,這點效率損失不成問題.

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在開發過程中,我們可能會遇到需要生成word,或者通過模板word替換相應內容的需求。但在文檔中插入圖片時,如果段落格式設置不對,就會導致圖片只顯示一點點或者不顯示。接下來就介紹一下java編輯word和插入圖片需怎麼處理。 1.引入依賴 首先我們在項目中引入Apache POI,用於讀取和操作wo ...
  • 1. Spring MVC 中的攔截器的使用“攔截器基本配置” 和 “攔截器高級配置” @目錄1. Spring MVC 中的攔截器的使用“攔截器基本配置” 和 “攔截器高級配置”2. 攔截器3. Spring MVC 中的攔截器的創建和基本配置3.1 定義攔截3.2 攔截器基本配置3.3 攔截器的 ...
  • 條形碼和二維碼是現代信息交換和數據存儲的重要工具,它們將信息以圖形的形式編碼,便於機器識別和數據處理,被廣泛應用於物流、零售、醫療、教育等各領域。本文將介紹如何使用Python快速生成各種常見的條形碼如Code 128、EAN-13,以及生成二維碼。 Python條碼庫 本文需要用到 Spire.B ...
  • 以下是一個詳細全面的 Spring Boot 使用 WebSocket 的知識點彙總 1. 配置 WebSocket 添加依賴 進入maven官網, 搜索spring-boot-starter-websocket,選擇版本, 然後把依賴複製到pom.xml的dependencies標簽中 配置 We ...
  • 引言 在現代的互聯網應用中,數據安全和隱私保護變得越來越重要。尤其是在介面返回數據時,如何有效地對敏感數據進行脫敏處理,是每個開發者都需要關註的問題。本文將通過一個簡單的Spring Boot項目,介紹如何實現介面數據脫敏。 一、介面數據脫敏概述 1.1 介面數據脫敏的定義 介面數據脫敏是指在介面返 ...
  • 本文介紹基於R語言中的UBL包,讀取.csv格式的Excel表格文件,實現SMOTE演算法與SMOGN演算法,對機器學習、深度學習回歸中,訓練數據集不平衡的情況加以解決的具體方法~ ...
  • 什麼是I8n 國際化(I18n)指的是設計和開發產品的過程,使得它們能夠適應多種語言和文化環境,而不需要進行大量的代碼更改。這通常涉及到創建一個基礎版本的產品,然後通過配置和資源文件來添加對不同語言和地區的支持。 這樣,當產品需要在新的地理區域或語言環境中使用時,只需要添加或更新相應的資源文件,而不 ...
  • 目錄<future>future模板類成員函數:promise類promise的使用常式:packaged_task模板類常式:async模板函數常式:shared_future模板類 <future> 標準庫提供了一些工具來獲取非同步任務(即在單獨的線程中啟動的函數)的返回值,並捕捉其所拋出的異常。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...