日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

C 条件变量使用详解

發布時間:2023/12/2 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C 条件变量使用详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

condition_variable介紹

在C 11中,我們可以使用條件變量(condition_variable)實現多個線程間的同步操作;當條件不滿足時,相關線程被一直阻塞,直到某種條件出現,這些線程才會被喚醒。

其主要成員函數如下:

條件變量是利用線程間共享的全局變量進行同步的一種機制,主要包括兩個動作:

  • 一個線程因等待"條件變量的條件成立"而掛起;

  • 另外一個線程使"條件成立",給出信號,從而喚醒被等待的線程。

為了防止競爭,條件變量的使用總是和一個互斥鎖結合在一起;通常情況下這個鎖是std::mutex,并且管理這個鎖 只能是 std::unique_lockstd::mutex RAII模板類。

上面提到的兩個步驟,分別是使用以下兩個方法實現:

  • 等待條件成立使用的是condition_variable類成員wait 、wait_for 或 wait_until。

  • 給出信號使用的是condition_variable類成員notify_one或者notify_all函數。

細節說明

在條件變量中只能使用std::unique_lock< std::mutex >說明

unique_lock和lock_guard都是管理鎖的輔助類工具,都是RAII風格;它們是在定義時獲得鎖,在析構時釋放鎖。它們的主要區別在于unique_lock鎖機制更加靈活,可以再需要的時候進行lock或者unlock調用,不非得是析構或者構造時。它們的區別可以通過成員函數就可以一目了然。在這里插入圖片描述

wait/wait_for說明

線程的阻塞是通過成員函數wait()/wait_for()/wait_until()函數實現的。這里主要說明前面兩個函數:

wait()成員函數

函數聲明如下:

void?wait(?std::unique_lock<std::mutex>&?lock?); //Predicate?謂詞函數,可以普通函數或者lambda表達式 template<?class?Predicate?> void?wait(?std::unique_lock<std::mutex>&?lock,?Predicate?pred?);

wait 導致當前線程阻塞直至條件變量被通知,或虛假喚醒發生,可選地循環直至滿足某謂詞。

wait_for()成員函數

函數聲明如下:

template<?class?Rep,?class?Period?> std::cv_status?wait_for(?std::unique_lock<std::mutex>&?lock,const?std::chrono::duration&?rel_time);template<?class?Rep,?class?Period,?class?Predicate?> bool?wait_for(?std::unique_lock<std::mutex>&?lock,const?std::chrono::duration&?rel_time,Predicate?pred);

wait_for 導致當前線程阻塞直至條件變量被通知,或虛假喚醒發生,或者超時返回。

返回值說明:

  • 若經過 rel_time 所指定的關聯時限則為 std::cv_status::timeout ,否則為 std::cv_status::no_timeout 。

  • 若經過 rel_time 時限后謂詞 pred 仍求值為 false 則為 false ,否則為 true 。

  • 以上兩個類型的wait函數都在會阻塞時,自動釋放鎖權限,即調用unique_lock的成員函數unlock(),以便其他線程能有機會獲得鎖。這就是條件變量只能和unique_lock一起使用的原因,否則當前線程一直占有鎖,線程被阻塞。

    notify_all/notify_one

    notify函數聲明如下:

    void?notify_one()?noexcept;

    若任何線程在 *this 上等待,則調用 notify_one 會解阻塞(喚醒)等待線程之一。

    void?notify_all()?noexcept;

    若任何線程在 *this 上等待,則解阻塞(喚醒)全部等待線程。

    虛假喚醒

    在正常情況下,wait類型函數返回時要不是因為被喚醒,要不是因為超時才返回,但是在實際中發現,因此操作系統的原因,wait類型在不滿足條件時,它也會返回,這就導致了虛假喚醒。因此,我們一般都是使用帶有謂詞參數的wait函數,因為這種(xxx, Predicate pred )類型的函數等價于:

    while?(!pred())?//while循環,解決了虛假喚醒的問題 {wait(lock); }

    原因說明如下:

    假設系統不存在虛假喚醒的時,代碼形式如下:

    if?(不滿足xxx條件) {//沒有虛假喚醒,wait函數可以一直等待,直到被喚醒或者超時,沒有問題。//但實際中卻存在虛假喚醒,導致假設不成立,wait不會繼續等待,跳出if語句,//提前執行其他代碼,流程異常wait();?? }//其他代碼 ...

    正確的使用方式,使用while語句解決:

    while?(!(xxx條件)?) {//虛假喚醒發生,由于while循環,再次檢查條件是否滿足,//否則繼續等待,解決虛假喚醒wait();?? } //其他代碼 ....

    條件變量使用

    在這里,我們使用條件變量,解決生產者-消費者問題,該問題主要描述如下:

    生產者-消費者問題,也稱有限緩沖問題,是一個多進程/線程同步問題的經典案例。該問題描述了共享固定大小緩沖區的兩個進程/線程——即所謂的“生產者”和“消費者”,在實際運行時會發生的問題。

    生產者的主要作用是生成一定量的數據放到緩沖區中,然后重復此過程。與此同時,費者也在緩沖區消耗這些數據。該問題的關鍵就是要保證生產者不會在緩沖區滿時加入數據,消費者也不會在緩沖區中空時消耗數據。

    要解決該問題,就必須讓生產者在緩沖區滿時休眠(要么干脆就放棄數據),等到下次消費者消耗緩沖區中的數據的時候,生產者才能被喚醒,開始往緩沖區添加數據。

    同樣,也可以讓消費者在緩沖區空時進入休眠,等到生產者往緩沖區添加數據之后,再喚醒消費者。

    生產者-消費者代碼如下:

    std::mutex?g_cvMutex; std::condition_variable?g_cv;//緩存區 std::deque<int>?g_data_deque; //緩存區最大數目 const?int??MAX_NUM?=?30; //數據 int?g_next_index?=?0;//生產者,消費者線程個數 const?int?PRODUCER_THREAD_NUM??=?3; const?int?CONSUMER_THREAD_NUM?=?3;void??producer_thread(int?thread_id) {while?(true){std::this_thread::sleep_for(std::chrono::milliseconds(500));//加鎖std::unique_lock?<std::mutex>?lk(g_cvMutex);//當隊列未滿時,繼續添加數據g_cv.wait(lk,?[](){?return?g_data_deque.size()?<=?MAX_NUM;?});g_next_index ;g_data_deque.push_back(g_next_index);std::cout?<<?"producer_thread:?"?<<?thread_id?<<?"?producer?data:?"?<<?g_next_index;std::cout?<<?"?queue?size:?"?<<?g_data_deque.size()?<<?std::endl;//喚醒其他線程?g_cv.notify_all();//自動釋放鎖} }void??consumer_thread(int?thread_id) {while?(true){std::this_thread::sleep_for(std::chrono::milliseconds(550));//加鎖std::unique_lock?<std::mutex>?lk(g_cvMutex);//檢測條件是否達成g_cv.wait(?lk,???[]{?return?!g_data_deque.empty();?});//互斥操作,消息數據int?data?=?g_data_deque.front();g_data_deque.pop_front();std::cout?<<?"\tconsumer_thread:?"?<<?thread_id?<<?"?consumer?data:?";std::cout?<<?data?<<?"?deque?size:?"?<<?g_data_deque.size()?<<?std::endl;//喚醒其他線程g_cv.notify_all();//自動釋放鎖} }int?main()

    總結

    以上是生活随笔為你收集整理的C 条件变量使用详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。