C++多線程快速入門(三):生產者消費者模型與條件變量使用

互斥鎖完成

#include <iostream>
#include <deque>
#include <thread>
#include <mutex>std::deque<int> q;
std::mutex mtx;static void produce(int val) {while(val--) {std::unique_lock<std::mutex> guard(mtx);q.push_front(val);mtx.unlock();std::this_thread::sleep_for(std::chrono::seconds(1));}
}
static void consumer() {int data = INT_MAX;while(data != 0) {std::unique_lock<std::mutex> guard(mtx);if (!q.empty()) {data = q.back();q.pop_back();std::cout << data << std::endl;mtx.unlock();} else  {mtx.unlock();}}
}
void test() {std::thread t1(produce,3);std::thread t2(consumer);t1.join();t2.join();
}int main() {test();return 0;
}

效果如下:

9
8
7
6
5
4
3
2
1
0
Process finished with exit code 1

produce在生產過程中,std::this_thread::sleep_for (std::chrono::seconds(1));表示延時1s,所以生產過程很慢。
consumer存在著一個while循環,只有在接收到表示結束的數據的時候,才會停止,每次循環內部,都是先加鎖,判斷隊列不空,然后就取出一個數,最后解鎖。這樣其實做了很多無用功,并且CPU占用率很高
可以在consumer內部也加一個小延時,在一次判斷后,如果發現隊列是空的,那就懲罰一下自己,延時一下,減少CPU的占用率。

static void consumer() {int data = INT_MAX;while(data != 0) {std::unique_lock<std::mutex> guard(mtx);if (!q.empty()) {data = q.back();q.pop_back();std::cout << data << std::endl;mtx.unlock();} else {mtx.unlock();std::this_thread::sleep_for(std::chrono::milliseconds(500));}}
}

條件變量改進模型

c++11提供了#include <condition_variable>頭文件,std::condition_variable可以和std::mutex結合一起使用,其中有兩個重要的接口,notify_one()wait()
wait()可以讓線程陷入休眠狀態,在消費者生產者模型中,如果生產者發現隊列中沒有東西,就可以讓自己休眠.notify_one()就是喚醒處于wait中的其中一個條件變量.
那什么時刻使用notify_one()比較好呢,當然是在生產者往隊列中放數據的時候了,隊列中有數據,就可以趕緊叫醒等待中的線程起來干活了。
下面是主要修改代碼:

std::condition_variable cond;static void produce(int val) {while(val--) {std::unique_lock<std::mutex> guard(mtx);q.push_front(val);mtx.unlock();cond.notify_one();  // 提醒一個waiting的線程std::this_thread::sleep_for(std::chrono::seconds(1));}
}
static void consumer() {int data = INT_MAX;while(data != 0) {std::unique_lock<std::mutex> guard(mtx);// 如果隊列為空,就一直等直到被notify_one喚醒while(q.empty())cond.wait(guard);data = q.back();q.pop_back();mtx.unlock();std::cout << data << std::endl;}
}

此時CPU的占用率也很低,因為在消費者端,隊列為空時,將控制權交給了cpu,直到被喚醒。
需要注意的是在判斷隊列是否為空的時候,使用的是while(q.empty()),而不是if(q.empty())
這是因為wait()從阻塞到返回,不一定就是由于notify_one()函數造成的,還有可能由于系統的不確定原因喚醒(可能和條件變量的實現機制有關),這個的時機和頻率都是不確定的,被稱作偽喚醒,如果在錯誤的時候被喚醒了,執行后面的語句就會錯誤,所以需要再次判斷隊列是否為空,如果還是為空,就繼續wait()阻塞。
在管理互斥鎖的時候,使用的是std::unique_lock而不是std::lock_guard,在上一篇筆記C++多線程快速入門(二)共享數據同步以及數據競爭中,談到過ock_guard沒有lock和unlock接口,而unique_lock提供了。這里的話也是由于此點原因。因為在wait()函數之前,使用互斥鎖保護了,如果wait的時候什么都沒做,豈不是一直持有互斥鎖?那生產者也會一直卡住,不能夠將數據放入隊列中了。所以,wait()函數會先調用互斥鎖的unlock()函數,然后再將自己睡眠,在被喚醒后,又會繼續持有鎖,保護后面的隊列操作。
另外除了notify_one()函數,c++還提供了notify_all()函數,可以同時喚醒所有處于wait狀態的條件變量。

參考

https://blog.csdn.net/qq_43145072/article/details/103732176

往期內容回顧

C++多線程快速入門(二)共享數據同步以及數據競爭
C++多線程快速入門(一):基本&常用操作

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

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

相關文章

【blade利刃出鞘】一起進入移動端webapp開發吧

前言 在移動浪潮襲來的時候&#xff0c;小釵有幸進入框架組做webapp框架開發&#xff0c;過程中遇到了移動端的各種坑&#xff0c;也產生了各種激情&#xff0c;就我們公司的發展歷程來說 第一階段&#xff1a;使用傳統方式開發移動站點&#xff0c;少量引入HTML5元素 第二階段…

題目理解。。

有時候一道大水題&#xff0c;因為英文描述可能有點復雜或者其它云云&#xff0c; 就比如那道PRO。。 別先被一道題嚇一跳&#xff0c;&#xff0c;&#xff0c; 英文描述看深入一點總歸會能解決的&#xff0c;盡可能多的考慮情況。大膽嘗試。轉載于:https://www.cnblogs.com/c…

C++多線程快速入門(四)shared_mutex以及讀寫鎖應用

在前面的三講中我們使用的mutex都是普通的std::mutex&#xff0c;這里介紹一下shared_mutex&#xff0c;版本為C17 std::shared_mutex的底層實現時操作系統提供的讀寫鎖&#xff0c;在讀多寫少的情況下&#xff0c;該shared_mutex比mutex更加高效。 它提供了常用的四種方法&am…

Tornado/Python 學習筆記(一)

1.源代碼下載及安裝&#xff1a;http://www.tornadoweb.org/en/stable/ 2.python中xmlrpc庫官方文檔&#xff1a;https://docs.python.org/3/library/xmlrpc.html?highlightxmlrpc 3.xml介紹與學習&#xff1a;http://www.w3school.com.cn/xml/xml_intro.asp XML 被設計為傳輸…

spring-aop-annotation

1。假設你已經配好依賴注入那一塊。此時的bean.xml為 <?xml version"1.0" encoding"UTF-8"?><beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xmlns:cont…

c++實現簡單線程池代碼

目錄完整代碼TaskPool.cppTaskPool.hmain.cpp完整代碼 TaskPool.cpp // // Created by LENOVO on 2021/10/25. //#include "TaskPool.h" #include <functional>std::mutex printMutex;TaskPool::TaskPool() : m_bRunning(false) {}TaskPool::~TaskPool() {re…

Android靜態圖片人臉識別的完整demo(附完整源碼)

Demo功能&#xff1a;利用android自帶的人臉識別進行識別&#xff0c;標記出眼睛和人臉位置。點擊按鍵后進行人臉識別&#xff0c;完畢后顯示到imageview上。 第一部分&#xff1a;布局文件activity_main.xml [html] view plaincopyprint?<RelativeLayout xmlns:android&qu…

圖論:最短路徑搜索--Dijkstra算法(c代碼實現)

最近因為辭職&#xff0c;有不少閑功夫&#xff0c;重溫下數據結構&#xff0c;順便練練手。今天說說最短路徑搜索算法中的Dijkstra原理和實現。 一&#xff1a;簡介 這個算法用于解決圖中單源最短路徑問題。所謂單源節點是指給定源節點&#xff0c;求圖中其它節點到此源節點的…

C++多線程快速入門(五)簡單線程池設計

目錄設計思路主線程運行邏輯task以及taskpool設計詳細流程講解完整代碼打印結果往期回顧設計思路 線程池實際上就是一組線程&#xff0c;當我們需要異步執行一些任務時&#xff0c;經常要通過OS頻繁創建和銷毀線程&#xff0c;不如直接創建一組在程序生命周期內不會退出的線程…

C--函數

函數:具有特定功能的代碼段,分為庫函數,自定義函數. 函數定義: 函數返回值類型 函數名(形式參數列表) { 代碼段; return 返回值; } 注意:每個函數返回值最多只有一個.return是一個函數結束的標志. 形式參數(形參):函數定義時使用的虛擬參數名,用以接收函數調用是傳遞過來的實際…

公式系統 - TradeBlazer公式基礎 - Bar數據

Bar數據 在介紹Bar數據之前&#xff0c;首先&#xff0c;我們需要討論一下TradeBlazer公式的計算方法&#xff0c;針對上面介紹的各種公式類型&#xff0c;包含公式應用&#xff0c;在公式進行計算時&#xff0c;都是建立在基本數據源(Bar數據)之上&#xff0c;我們這里所謂的B…

C++網絡編程快速入門(一):TCP網絡通信基本流程以及基礎函數使用

目錄流程概述服務器端代碼實現客戶端代碼實現函數和結構講解sockaddr_in和sockaddrsocket &#xff1a; 創建一個socket連接bind &#xff1a;綁定地址以及端口號問題流程概述 客戶端與服務器之間的網絡通信基本原理如下所示&#xff0c;復雜一點的架構可能會添加消息中間件。…

php 字符串處理

addcslashes — 為字符串里面的部分字符添加反斜線轉義字符addslashes — 用指定的方式對字符串里面的字符進行轉義bin2hex — 將二進制數據轉換成十六進制表示chop — rtrim() 的別名函數chr — 返回一個字符的ASCII碼chunk_split — 按一定的字符長度將字符串分割成小塊conve…

使用前端框架Foundation 4來幫助簡化響應式設計開發

日期&#xff1a;2013-3-12 來源&#xff1a;GBin1.com Foundation是一套使用廣泛的前端開發套件&#xff0c;可以幫助你快速的網站。最近ZURB發布了一個新版本的Foundation 4前端框架&#xff0c;能夠有效的幫助你快速的開發響應式的網站。 和另外一個套知名的前端框架BootSt…

C++網絡編程快速入門(二):Linux下使用select演示簡單服務端程序

目錄select參數解釋select使用規范select使用缺點基本流程實例代碼通信效果演示往期文章select參數解釋 extern int select (int __nfds, fd_set *__restrict __readfds,fd_set *__restrict __writefds,fd_set *__restrict __exceptfds,struct timeval *__restrict __timeout)…

Android轉載一:Android文件命名規范

REF&#xff1a;http://blog.csdn.net/gulianchao/article/details/23391651 (一) Layout命名 1&#xff0e;contentview命名&#xff1a;activity_功能模塊.xml 例如&#xff1a;activity_main.xml、activity_more.xml 2&#xff0e;Dialog命名&#xff1a;dialog_描述.xml …

[轉]XBRL應用軟件分類

1) 分類標準編輯軟件(Taxonomy editor)&#xff1a; 分類標準是XBRL技術的應用基礎&#xff0c;每一個采用XBRL技術的國家都必須先按各國的GAAP制訂XBRL分類標準&#xff0c;上市公司才能據以編制實例文件。由于一套XBRL 2.0或2.1版分類標準必須包含至少一份XML Schema文…

C++網絡編程快速入門(三):阻塞與非阻塞式調用網絡通信函數

目錄阻塞與非阻塞定義send與recvconnect一些問題為什么要將監聽socket設置為非阻塞阻塞與非阻塞定義 阻塞模式指的是當前某個函數執行效果未達預期&#xff0c;該函數會阻塞當前的執行線程&#xff0c;程序執行流在超時時間到達或者執行成功后恢復原有流程。非阻塞模式相反&am…

css3實現頭像旋轉360度

css樣式: .div a img{ width: 88px; height: 88px; border-radius: 88px; transition: all 1.2s ease-out 0s;}.div a img:hover{ -webkit-transform:rotate(360deg); -moz-transform:rotate(360deg); -o-transform:rotate(360deg); -ms-transform:rotate(360deg); transform:r…

POJ 2488 深搜

POJ 2488 深搜 要求字典序的順序。 1 #include <iostream>2 #include <stdio.h>3 #include <string.h>4 using namespace std;5 int n,m,cnt;6 bool success;7 bool sign[30][30];8 int step[30][2];9 int dir[8][2]{ 10 -2,-1,-2,1, 11 …