Linux多線程(十二)之【生產者消費者模型】

文章目錄

    • 生產者消費者模型
      • 為何要使用生產者消費者模型
        • 生產者消費者模型優點
      • 基于BlockingQueue的生產者消費者模型
        • BlockingQueue
        • C++ queue模擬阻塞隊列的生產消費模型
          • 單線程生產消費模型
          • 多線程生產消費模型

生產者消費者模型

consumer/productor

321原則

321原則(便于記憶)

為何要使用生產者消費者模型

生產者消費者模式就是通過一個容器來解決生產者和消費者的強耦合問題。

生產者和消費者彼此之間不直接通訊,而通過阻塞隊列來進行通訊,

所以生產者生產完數據之后不用等待消費者處理,直接扔給阻塞隊列,

消費者不找生產者要數據,而是直接從阻塞隊列里取,

阻塞隊列就相當于一個緩沖區,平衡了生產者和消費者的處理能力。

這個阻塞隊列就是用來給生產者和消費者解耦的。

生產者消費者模型優點
  1. 解耦
  2. 支持并發
  3. 支持忙閑不均

image-20250425234123376

生產消費模型的高效問題

cp問題高效

基于BlockingQueue的生產者消費者模型

BlockingQueue

在多線程編程中阻塞隊列(Blocking Queue)是一種常用于實現生產者和消費者模型的數據結構。

其與普通的隊列區別在于,

當隊列為空時,從隊列獲取元素的操作將會被阻塞,直到隊列中被放入了元素;

當隊列滿時,往隊列里存放元素的操作也會被阻塞,直到有元素被從隊列中取出

(以上的操作都是基于不同的線程來說的,線程在對阻塞隊列進程操作時會被阻塞)

image-20250425234454539

C++ queue模擬阻塞隊列的生產消費模型

代碼:

單線程生產消費模型

blockqueue.hpp

#pragma once#include <iostream>
#include <pthread.h>
#include <queue>using namespace std;template <class T>
class blockqueue
{static const int defaultnum = 20;public:blockqueue(int maxcap = defaultnum): maxcap_(maxcap){pthread_mutex_init(&mutex_, nullptr);pthread_cond_init(&c_cond_, nullptr);pthread_cond_init(&p_cond_, nullptr);low_water=maxcap_/3;high_water=maxcap_*2/3;}~blockqueue(){pthread_mutex_destroy(&mutex_);pthread_cond_destroy(&c_cond_);pthread_cond_destroy(&p_cond_);}T pop(){pthread_mutex_lock(&mutex_);if(q_.size()==0){pthread_cond_wait(&c_cond_,&mutex_);//1.調用的時候,自動釋放鎖}T t=q_.front();             // 你想消費,就直接能消費嗎?不一定。你得先確保消費條件滿足q_.pop();if(q_.size()<low_water)pthread_cond_signal(&p_cond_);pthread_mutex_unlock(&mutex_);return t;}void push(const T &in){pthread_mutex_lock(&mutex_);if(q_.size()==maxcap_){pthread_cond_wait(&p_cond_,&mutex_);//1.調用的時候,自動釋放鎖}q_.push(in);                // 你想生產,就直接能生產嗎?不一定。你得先確保生產條件滿足if(q_.size()>high_water)pthread_cond_signal(&c_cond_);pthread_mutex_unlock(&mutex_);}private:queue<T> q_;int maxcap_;// int mincap_;pthread_mutex_t mutex_;pthread_cond_t c_cond_;pthread_cond_t p_cond_;int low_water;int high_water;
};

main.cc

#include<iostream>
#include"blockqueue.hpp"
#include<unistd.h>using namespace std;void*Consumer(void*args)
{blockqueue<int> *bq=static_cast<blockqueue<int> *>(args);while(1){int data=bq->pop();cout<<"消費了一個數據: "<<data<<endl;// sleep(1);}
}void*Productor(void*args)
{blockqueue<int> *bq=static_cast<blockqueue<int> *>(args);int data=0;while(1){data++;bq->push(data);cout<<"生產了一個數據: "<<data<<endl;sleep(1);}
}int main()
{blockqueue<int> *bq=new blockqueue<int>();pthread_t c,p;pthread_create(&c,nullptr,Consumer,bq);pthread_create(&p,nullptr,Productor,bq);pthread_join(c,nullptr);pthread_join(p,nullptr);delete bq;return 0;
}

image-20250508225919230

消費者sleep

image-20250508230034577

兩者都不sleep

image-20250508230244057

多線程生產消費模型

補充:

  1. 為什么判斷條件要放到加鎖之后?

因為判斷臨界資源條件是否滿足,也是在訪問臨界資源!

判斷臨界資源是否就緒,是通過在臨界區內部判斷的!

  1. 如果臨界資源未就緒,那么線程就要進行等待。

等待的時候,線程是持有鎖的!所以調用wait時,自動釋放鎖。

如果不釋放鎖,直接等待,那么等待的線程就沒有線程可以喚醒了,

因為其他線程都在鎖外,進不去臨界區。

該線程因為喚醒而返回的時候,重新持有鎖了。

  1. 如果線程在wait時,被誤喚醒了呢?

偽喚醒的概念

偽喚醒

解決方法:判斷條件時用while,不用if

blockqueue.hpp

#pragma once#include <iostream>
#include <pthread.h>
#include <queue>using namespace std;template <class T>
class blockqueue
{static const int defaultnum = 20;public:blockqueue(int maxcap = defaultnum): maxcap_(maxcap){pthread_mutex_init(&mutex_, nullptr);pthread_cond_init(&c_cond_, nullptr);pthread_cond_init(&p_cond_, nullptr);// low_water=maxcap_/3;// high_water=maxcap_*2/3;}~blockqueue(){pthread_mutex_destroy(&mutex_);pthread_cond_destroy(&c_cond_);pthread_cond_destroy(&p_cond_);}T pop(){pthread_mutex_lock(&mutex_);while(q_.size()==0)//做到防止代碼被偽喚醒!{pthread_cond_wait(&c_cond_,&mutex_);//1.調用的時候,自動釋放鎖}T t=q_.front();             // 你想消費,就直接能消費嗎?不一定。你得先確保消費條件滿足q_.pop();// if(q_.size()<low_water)pthread_cond_signal(&p_cond_);pthread_cond_signal(&p_cond_);//pthread_cond_broadcastpthread_mutex_unlock(&mutex_);return t;}void push(const T &in){pthread_mutex_lock(&mutex_);while(q_.size()==maxcap_)//做到防止代碼被偽喚醒!{pthread_cond_wait(&p_cond_,&mutex_);//1.調用的時候,自動釋放鎖}q_.push(in);                // 你想生產,就直接能生產嗎?不一定。你得先確保生產條件滿足// if(q_.size()>high_water)pthread_cond_signal(&c_cond_);pthread_cond_signal(&c_cond_);pthread_mutex_unlock(&mutex_);}private:queue<T> q_;int maxcap_;// int mincap_;pthread_mutex_t mutex_;pthread_cond_t c_cond_;pthread_cond_t p_cond_;// int low_water;// int high_water;
};

Task.hpp

#pragma once
#include <iostream>
#include<string>
using namespace std;enum
{Div_zero = 1,Mod_zero,Unknown
};class Task
{
public:Task(int x, int y, char op): a(x), b(y), op_(op), ret(0), exitcode(0){}void Run(){switch (op_){case '+':ret = a + b;break;case '-':ret = a - b;break;case '*':ret = a * b;break;case '/':{if (b == 0)exitcode = Div_zero;elseret = a / b;}break;case '%':{if (b == 0)exitcode = Mod_zero;elseret = a % b;}break;default:exitcode=Unknown;break;}}string GetTask(){string r=to_string(a);r+=op_;r+=to_string(b);r+="=???";return r;}string Getret(){string r=to_string(a);r+=op_;r+=to_string(b);r+="=";r+=to_string(ret);r+=" [ exitcode: ";r+=to_string(exitcode);r+=" ]";return r;}void operator()(){Run();}~Task() {}private:int a;int b;char op_;int ret;int exitcode;
};

main.cc

#include <iostream>
#include "blockqueue.hpp"
#include <unistd.h>
#include "Task.hpp"
#include <ctime>using namespace std;string oper = "+-*/%";void *Consumer(void *args)
{blockqueue<Task> *bq = static_cast<blockqueue<Task> *>(args);while (1){Task data = bq->pop();// data.Run();// 計算// data.Run();data();cout << "處理了一個任務 , 運算結果為: " << data.Getret() << " ,thread id: " << pthread_self() << endl;// sleep(1);}
}void *Productor(void *args)
{int len = oper.size();blockqueue<Task> *bq = static_cast<blockqueue<Task> *>(args);// int x=0,y=0;// Task data(x,y);while (1){// 模擬生產者生產數據int x = rand() % 10 + 1; //[1,10]int y = rand() % 10;     //[0,9];char op = oper[rand() % len];Task data(x, y, op);usleep(10);// 計算bq->push(data);cout << "生產了一個任務: " << data.GetTask() << " ,thread id: " << pthread_self() << endl;sleep(1);}
}int main()
{srand(time(nullptr));blockqueue<Task> *bq = new blockqueue<Task>();pthread_t c[3], p[5];for (int i = 0; i < 3; i++){pthread_create(c + i, nullptr, Consumer, bq);}for (int i = 0; i < 5; i++){pthread_create(p + i, nullptr, Productor, bq);}for (int i = 0; i < 3; i++){pthread_join(c[i], nullptr);}for (int i = 0; i < 5; i++){pthread_join(p[i], nullptr);}delete bq;return 0;
}

image-20250511010553986

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

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

相關文章

MySQL表的操作(3)

文章目錄前言一、創建表創建表時指定屬性二、查看表查看表結構查看建表消息三、修改表修改列屬性修改列名修改表名四、刪除表總結前言 Hello! 那我們乘勝追擊&#xff0c;開始 表的操作&#xff01; 一、創建表 首先創建一個 數據庫 testForTable mysql> create database i…

從“人工智障”到“智能助手”:集成為什么能拯救AI用戶體驗?

幾年前&#xff0c;當人們滿懷期待地與AI語音助手對話時&#xff0c;常常遭遇令人啼笑皆非的回應——“抱歉&#xff0c;我不明白你在說什么”“請再說一遍”甚至答非所問。AI被戲稱為“人工智障”&#xff0c;用戶體驗一度讓人失望。然而&#xff0c;近年來&#xff0c;隨著技…

Uniapp 自定義TabBar + 動態菜單實現教程(Vuex狀態管理詳解)

大家好&#xff0c;我是一諾。今天跟大家分享一下uniapp 封裝自定義底部導航欄&#xff08;TabBar&#xff09; 過程中的思考和實踐。通過本文&#xff0c;你將學會如何打造一個功能完善、可自由定制的TabBar組件&#xff01; 先看效果&#xff1a; 支持自定義圖標和樣式動態顯…

MySQL數據庫主從復制

概述1、master開啟二進制日志記錄2、slave開啟IO進程&#xff0c;從master中讀取二進制日志并寫入slave的中繼日志3、slave開啟SQL進程&#xff0c;從中繼日志中讀取二進制日志并進行重放4、最終&#xff0c;達到slave與master中數據一致的狀態&#xff0c;我們稱作為主從復制的…

Rancher Server + Kubernets搭建云原生集群平臺

目錄Rancher Server Kubernets搭建云原生集群平臺一、環境準備1、軟件準備2、環境規劃3、掛載數據盤二、虛擬機初始化基礎配置&#xff08;所有節點都需要操作&#xff09;1、執行時間服務器腳本&#xff08;包括配置hostName主機名&#xff09;2、配置hosts文件3、配置各節點…

Java學習第八部分——泛型

目錄 一、概述 &#xff08;一&#xff09;定義 &#xff08;二&#xff09;作用 &#xff08;三&#xff09;引入原因 二、使用 &#xff08;一&#xff09;類 &#xff08;二&#xff09;接口 &#xff08;三&#xff09;方法 三、類型參數 &#xff08;一&#xf…

定時點擊二次鼠標 定時點擊鼠標

定時點擊二次鼠標 定時點擊鼠標 今天分享一個定時點擊兩次的小工具。 我們在生活中&#xff0c;可能會遇到一些定時點擊的任務。比如說在晚上9點去發送一個群發&#xff0c;或者倒計時點擊一個按鈕。那么可以使用這個工具&#xff0c;僅適用于Windows電腦。 #定時點擊鼠標 #倒計…

Linux網絡配置與故障排除完全指南

1. ifconfig命令 - 網絡接口配置器 ifconfig&#xff08;interface configurator&#xff09;是Linux系統中最基礎的網絡配置工具。該命令可以初始化網絡接口、分配IP地址、啟用或禁用接口&#xff0c;同時還能查看接口的詳細信息。 查看網絡接口信息 # ifconfig eth0 …

Python Pytest-Benchmark詳解:精準性能測試的利器

在軟件開發的迭代過程中&#xff0c;性能優化如同精密手術&#xff0c;需要精準的測量工具。Pytest-Benchmark作為pytest生態中的性能測試插件&#xff0c;憑借其無縫集成能力和專業統計功能&#xff0c;成為Python開發者進行基準測試的首選工具。本文將深入解析其技術特性與實…

60天python訓練營打卡day51

學習目標&#xff1a; 60天python訓練營打卡 學習內容&#xff1a; DAY 51 復習日 作業&#xff1a;day43的時候我們安排大家對自己找的數據集用簡單cnn訓練&#xff0c;現在可以嘗試下借助這幾天的知識來實現精度的進一步提高 學習時間&#xff1a; 2025.07.04 浙大疏錦行…

支持向量機(SVM)在肺部CT圖像分類(肺癌檢測)中的實現與優化

?? 博主簡介:CSDN博客專家、CSDN平臺優質創作者,高級開發工程師,數學專業,10年以上C/C++, C#, Java等多種編程語言開發經驗,擁有高級工程師證書;擅長C/C++、C#等開發語言,熟悉Java常用開發技術,能熟練應用常用數據庫SQL server,Oracle,mysql,postgresql等進行開發應用…

YOLOv3-SPP 深度解析:引入 SPP 結構,顯著提升目標檢測性能!

? YOLOv3-SPP 技術詳解 一、前言 YOLOv3-SPP 是在 YOLOv3 基礎上加入 SPP&#xff08;Spatial Pyramid Pooling&#xff09;模塊的一種改進版本&#xff0c;旨在提升模型對不同尺度目標的識別能力&#xff0c;尤其是在大目標檢測方面表現更優。 它由 Alexey Bochkovskiy 在…

負載均衡--常見負載均衡算法

負載均衡算法可以分為兩類&#xff1a;靜態負載均衡算法和動態負載均衡算法。 1、靜態負載均衡算法包括&#xff1a;輪詢&#xff0c;比率&#xff0c;優先權 輪詢&#xff08;Round Robin&#xff09;&#xff1a;順序循環將請求一次順序循環地連接每個服務器。當其中某個服務…

深入解析GCC:開源的編譯器之王

在編程世界中&#xff0c;編譯器是將人類可讀代碼轉化為機器指令的關鍵橋梁。而GCC&#xff08;GNU Compiler Collection&#xff09; 無疑是這個領域最耀眼的明星之一。作為開源世界的基石&#xff0c;它支撐著Linux內核、眾多開源項目和商業軟件的構建。今天&#xff0c;我們…

https和http有什么區別

目錄 一、核心區別&#xff1a;是否基于加密傳輸 二、底層傳輸機制差異 三、HTTPS 的加密原理 四、應用場景差異 五、其他細節區別 總結 在網絡通信中&#xff0c;HTTP&#xff08;Hypertext Transfer Protocol&#xff0c;超文本傳輸協議&#xff09; 和HTTPS&#xff0…

CSS3 文本效果詳解

CSS3 文本效果詳解 引言 隨著Web技術的發展,CSS3為前端設計師和開發者提供了豐富的文本效果選項。這些效果不僅能夠增強網頁的美觀性,還能提升用戶體驗。本文將詳細介紹CSS3中的文本效果,包括文本陰影、文本描邊、文本裝飾、文本換行、文本大小寫等,并探討如何在實際項目…

MySQL 中 -> 和 ->> 操作符的區別

簡介 MySQL 5.7 或更高版本&#xff0c;可以使用 ->> 和 -> 運算符簡化語法這兩個操作符都是用于提取 JSON 數據的&#xff0c;但有一些重要區別 -> 操作符 功能&#xff1a;提取 JSON 對象的指定路徑的值 返回類型&#xff1a;返回 JSON 類型的值&#xff08;可…

Vue2 day07

1.vuex的基本認知2.構建多組件共享的數據環境步驟&#xff1a;1.在自己創建的文件夾下創建腳手架2.創建三個組件### 源代碼如下App.vue在入口組件中引入 Son1 和 Son2 這兩個子組件html <template><div id"app"><h1>根組件</h1><input ty…

簡述MCP的原理-AI時代的USB接口

1 簡介隨著AI的不斷發展&#xff0c;RAG&#xff08;檢索增強生成&#xff09;和function calling等技術的出現&#xff0c;使得大語言模型的對話生成能力得到了增強。然而&#xff0c;function calling的實現邏輯比較復雜&#xff0c;一個簡單的工具調用和實現方式需要針對不同…

CISSP知識點匯總-資產安全

CISSP知識點匯總 域1---安全與風險管理域2---資產安全域3---安全工程域4---通信與網絡安全域5---訪問控制域6---安全評估與測試域7---安全運營域8---應用安全開發域2 資產安全 一、資產識別和分類 1、信息分級(Classification): 按照敏感程度(機密性被破壞) 按照重要程度…