Linux :線程 【生產者消費者模型】

Linux :線程 【生產者消費者模型與信號量】

  • (一)生產消費模型
    • 1、生產消費模式概念
    • 2、生產者消費者之間的關系
    • 3、生產者消費者模型優點
  • (二)基于BlockingQueue的生產者消費者模型
    • 1、基于阻塞隊列模型
    • 2、模擬實現基于阻塞隊列的生產消費模型

(一)生產消費模型

1、生產消費模式概念

  • 生產者消費者模式就是通過一個容器來解決生產者和消費者的強耦合問題。
  • 生產者和消費者彼此之間不直接通訊,而通過阻塞隊列來進行通訊,所以生產者生產完數據之后不用等待消費者處理,直接扔給阻塞隊列,消費者不找生產者要數據,而是直接從阻塞隊列里取,阻塞隊列就相當于一個緩沖區,平衡了生產者和消費者的處理能力。
  • 這個阻塞隊列就是用來給生產者和消費者解耦的。

在這里插入圖片描述

2、生產者消費者之間的關系

生產者消費者模型是多線程同步與互斥的一個經典場景,最根本特點是 321原則:

  • 三種關系: 生產者和生產者(互斥關系)、消費者和消費者(互斥關系)、生產者和消費者(互斥關系、同步關系)。
  • 兩種角色: 生產者和消費者。(通常由進程或線程承擔)
  • 一個交易場所: 通常指的是內存中的一段緩沖區(阻塞隊列、環形隊列)。

為什么三種關系中都具有互斥關系??

因為容器(交易場所)會有多個執行流進行訪問,而我們要保持臨界資源的安全,需要加上互斥關系。

生產者和消費者之間為什么會存在同步關系?

如果讓生產者一直生產,那么當生產者生產的數據將容器塞滿后,生產者再生產數據就會生產失敗。
反之,讓消費者一直消費,那么當容器當中的數據被消費完后,消費者再進行消費就會消費失敗。

互斥關系保證的是數據的正確性,而同步關系是為了讓多線程之間協同起來。

3、生產者消費者模型優點

  • 解耦
    生產者、消費者、交易場所 各司其職,可以根據具體需求自由設計,很好地做到了 解耦,便于維護和擴展
  • 支持并發。
  • 支持忙閑不均。

生產者在生產時,無需關注消費者的狀態,只需關注交易場所中是否有空閑位置
消費者在消費時,無需關注生產者的狀態,只需關注交易場所中是否有就緒數據

(二)基于BlockingQueue的生產者消費者模型

1、基于阻塞隊列模型

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

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

  • 當隊列為空時,從隊列獲取元素的操作將會被阻塞,直到隊列中被放入了元素
  • 當隊列滿時,往隊列里存放元素的操作也會被阻塞,直到有元素被從隊列中取出
    (以上的操作都是基于不同的線程來說的,線程在對阻塞隊列進程操作時會被阻塞)

2、模擬實現基于阻塞隊列的生產消費模型

阻塞隊列實現代碼:

#include <iostream>
#include <queue>
#include <unistd.h>
#include <pthread.h>
#define NUM 5using namespace std;template <class T>
class BlockQueue
{
private:queue<T> _q;               // 阻塞隊列pthread_mutex_t _mutex;    // 互斥鎖pthread_cond_t _consumers; // 消費者條件變量pthread_cond_t _producer;  // 生產者條件變量int _capacity;             // 阻塞隊列容量
public://初始化鎖和條件變量BlockQueue(int capacity = NUM): _capacity(capacity){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_consumers, nullptr);pthread_cond_init(&_producer, nullptr);}//銷毀鎖和條件變量~BlockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_consumers);pthread_cond_destroy(&_producer);}//阻塞隊列里插入數據void push(const T &in){pthread_mutex_lock(&_mutex);//當阻塞隊列為空時不能再插入變量,需要做到同步,讓線程進入到生產者的條件變量中去while (_q.size() == _capacity) //這里一定要是while ,因為喚醒可能造成偽喚醒(如果同時喚醒條件變量中的所有線程,那么可能造成偽喚醒){                              pthread_cond_wait(&_producer, &_mutex);}_q.push(in);pthread_cond_signal(&_consumers); //push數據后,阻塞隊列一定有數據存在,所以喚醒消費者的條件變量。pthread_mutex_unlock(&_mutex);}//從阻塞隊列里拿出數據T pop(){pthread_mutex_lock(&_mutex);//當阻塞隊列為空時,執行該代碼的線程放入到消費者條件變量中while (_q.size() == 0){pthread_cond_wait(&_consumers, &_mutex);}T data = _q.front();_q.pop();pthread_cond_signal(&_producer); //pop數據后,阻塞隊列一定是滿的,喚醒生產者的條件變量pthread_mutex_unlock(&_mutex);return data;}
};

使用 互斥鎖 + 條件變量 實現互斥與同步


生產者和消費者模型中傳遞的數據一般都是要經過處理的,例如:

  • 生產者:
    生成數據 + 傳遞數據
  • 消費者:
    獲取數據 + 處理數據

為了方便理解我們簡單實現一個任務類,Task.hpp代碼如下:

#pragma once
#include <iostream>
#include <string>std::string opers = "+-*/%";enum // 規定錯誤碼
{DivZero = 1, // 當 除數為0時ModZero,     // 當 %的時候 ,xx % 0時Unknown      // 出現錯誤的符號時
};class Task
{
private:int data1_; //int data2_;char oper_;int exitcode_; // 錯誤碼,判斷結果是否合理,默認為0int result_;   // 結果public:Task(int x, int y, char op) : data1_(x), data2_(y), oper_(op), result_(0), exitcode_(0){}~Task(){}void run(){switch (oper_){case '+':result_ = data1_ + data2_;break;case '-':result_ = data1_ - data2_;break;case '*':result_ = data1_ * data2_;break;case '/':{if (data2_ == 0)exitcode_ = DivZero;elseresult_ = data1_ / data2_;}break;case '%':{if (data2_ == 0)exitcode_ = ModZero;elseresult_ = data1_ % data2_;}break;default:exitcode_ = Unknown;break;}}//兩數運算 所指向的任務std::string GetTask(){std::string r = std::to_string(data1_);r += oper_;r += std::to_string(data2_);r += "= ";return r;}//兩數運算的結果std::string GetResult(){std::string r = std::to_string(result_);r += "[code: ";r += std::to_string(exitcode_);r += "]";return r;}
};

該類主要是實現兩數之間的運算,使用該類時需要我們提供具體的 兩個數字 和 運算符。


接下來準備工作已經完畢,我們看看如何運用阻塞隊列,為了方便觀察數據我這里是單生產者單消費者的模型,main.cc代碼如下:

#pragma Once
#include "BlockQueue.hpp"
#include <vector>
#include <string>
#include <ctime>
#include "Task.hpp"// std::string opers = "+-*/%";
void *Consumer(void *args)
{BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);while (true){// 接收任務Task data = bq->pop();usleep(10000);// 處理任務data.run();cout << endl;cout << "處理了任務 " << data.GetTask() << data.GetResult();}return nullptr;
}void *Producer(void *args)
{BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);while (true){// 產生任務char op = opers[rand() % opers.size()];int data1 = rand() % 20 + 1;int data2 = rand() % 10;usleep(10);Task t(data1, data2, op);// 發送任務bq->push(t);cout << endl;cout << "產生了一個任務=> " << t.GetTask() << "??";sleep(1);}return nullptr;
}int main()
{srand(time(nullptr));BlockQueue<Task> *bq = new BlockQueue<Task>(5); //阻塞隊列//創造單生產者 單消費者pthread_t c, p;pthread_create(&c, nullptr, Producer, bq);pthread_create(&p, nullptr, Consumer, bq);pthread_join(p, nullptr);pthread_join(c, nullptr);delete bq;return 0;
}

運行效果如下:
在這里插入圖片描述

生產消費模型在代碼層的實際 就是線程的同步與互斥 。

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

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

相關文章

mac本地docker鏡像上傳指定虛擬機

在Mac本地將Docker鏡像上傳至指定虛擬機的完整步驟 1. 在Mac本地保存Docker鏡像為文件 通過docker save命令將鏡像打包為.tar文件&#xff0c;便于傳輸至虛擬機。 # 示例&#xff1a;保存名為"my_image"的鏡像到當前目錄 docker save -o my_image.tar my_image:ta…

C++跨平臺開發經驗與解決方案

在當今軟件開發領域&#xff0c;跨平臺開發已成為一個重要的需求。C作為一種強大的系統級編程語言&#xff0c;在跨平臺開發中扮演著重要角色。本文將分享在實際項目中的跨平臺開發經驗和解決方案。 1. 構建系統選擇 CMake的優勢 跨平臺兼容性好 支持多種編譯器和IDE 強大…

Void: Cursor 的開源平替

GitHub&#xff1a;https://github.com/voideditor/void 更多AI開源軟件&#xff1a;發現分享好用的AI工具、AI開源軟件、AI模型、AI變現 - 小眾AI Void&#xff0c;這款編輯器號稱是開源的 Cursor 和 GitHub Copilot 替代品&#xff0c;而且完全免費&#xff01; 在你的代碼庫…

基于HTML+JavaScript+CSS實現教學網站

摘要 21世紀是信息化的時代&#xff0c;信息化物品不斷地涌入我們的生活。同時&#xff0c;教育行業也產生了重大變革。傳統的身心教授的模式&#xff0c;正在被替代。互聯網模式的教育開辟了一片新的熱土。 這算是對教育行業的一次重大挑戰。截至目前&#xff0c;眾多教育行…

基于ssm+mysql的高校設備管理系統(含LW+PPT+源碼+系統演示視頻+安裝說明)

系統功能 管理員功能&#xff1a;系統登錄、員工管理、設備管理、設備采購統計、設備報廢統計&#xff1b;用戶角色功能&#xff1a;設備采購管理、設備報廢管理、個人資料管理。 作者&#xff1a;計算機搬磚家 開發技術&#xff1a;SpringBoot、php、Python、小程序、SSM、Vu…

電力桿塔安全監測解決方案

一、方案背景 在臺風、滑坡等自然災害出現時&#xff0c;極易產生倒桿、斷桿、桿塔傾斜、塔基滑動等致使桿塔失穩的狀況&#xff0c;進而引發導線斷線、線路跳閘等事故&#xff0c;給電網的安全穩定運行造成影響。可借助在鐵塔上裝設的傳感器&#xff0c;能夠感知鐵塔的工作狀態…

基于Quicker構建從截圖到公網圖像鏈接獲取的自動化流程

寫在前面&#xff1a;本博客僅作記錄學習之用&#xff0c;部分圖片來自網絡&#xff0c;如需引用請注明出處&#xff0c;同時如有侵犯您的權益&#xff0c;請聯系刪除&#xff01; 文章目錄 前言預備內容轉webp程序PicGo設置Quicker設置視頻演示總結互動致謝參考 前言 在自建博…

Python Requests庫完全指南:從入門到精通

引言 在Python的生態系統中&#xff0c;requests庫以其簡潔優雅的API設計和強大的功能&#xff0c;成為HTTP請求處理領域的標桿工具。無論是數據爬蟲開發、API接口調用&#xff0c;還是自動化測試場景&#xff0c;requests都能將復雜的網絡交互簡化為幾行可讀性極高的代碼。相…

滲透測試核心技術:內網滲透與橫向移動

內網滲透是紅隊行動的關鍵階段,攻擊者通過突破邊界進入內網后,需快速定位域控、橫向移動并維持權限。本節從內網環境搭建、信息收集、橫向移動技巧到權限維持工具,系統講解如何在內網中隱蔽行動并擴大戰果。 1. 內網環境搭建與基礎配置 目標: 模擬真實企業網絡,構建包含…

學習FineBI

FineBI 第一章 FineBI 介紹 1.1. FineBI 概述 FineBI 是帆軟軟件有限公司推出的一款商業智能 &#xff08;Business Intelligence&#xff09; 產品 。 FineBI 是新一代大數據分析的 BI 工具 &#xff0c; 旨在幫助企業的業務人員充分了解和利用他們的數據 。FineBI 憑借強…

CSS 浮動(Float)及其應用

1. 什么是浮動&#xff08;Float&#xff09;&#xff1f; 浮動元素會脫離正常的文檔流&#xff08;Document Flow&#xff09;&#xff0c;并向左或向右移動&#xff0c;直到碰到父元素的邊緣或另一個浮動元素。 基本語法 .float-left {float: left; }.float-right {float:…

二分算法的介紹簡單易懂

目錄 1.概論 2.樸素的二分算法 3.求左端點的二分算法和求右端點的二分算法 4.總結 1.概論 要想了解什么是二分算法&#xff0c;我們就要知道什么是二分算法&#xff0c;二分算法是根據數組的規律&#xff0c;每次查找的數據原來的效率可能要O&#xff08;n&#xff09;,而我…

ROS2學習(3)------架構概述

操作系統&#xff1a;ubuntu22.04 IDE:Visual Studio Code 編程語言&#xff1a;C11 ROS版本&#xff1a;2 ROS 2&#xff08;Robot Operating System 2&#xff09;的設計旨在提供一個靈活、可擴展且高效的框架&#xff0c;用于編寫復雜的機器人軟件。它引入了發布者/訂閱者&…

墨水屏顯示模擬器程序解讀

程序如下&#xff1a;出處https://github.com/tsl0922/EPD-nRF5?tabreadme-ov-file // GUI emulator for Windows // This code is a simple Windows GUI application that emulates the display of an e-paper device. #include <windows.h> #include <stdint.h>…

【技海登峰】Kafka漫談系列(十一)SpringBoot整合Kafka之消費者Consumer

【技海登峰】Kafka漫談系列(十一)SpringBoot整合Kafka之消費者Consumer spring-kafka官方文檔: https://docs.spring.io/spring-kafka/docs/2.8.10/reference/pdf/spring-kafka-reference.pdf KafkaTemplate API: https://docs.spring.io/spring-kafka/api/org/springframe…

【言語理解】邏輯填空之邏輯對應11

front&#xff1a;詞義辨析 11.1前后解釋對應 填空的詞匯大意可能是吖要結合實際情況不要一味高估導致適得其反的結果 未雨綢繆&#xff1a;趁著天沒下雨&#xff0c;先修繕房屋門窗。比喻事先做好準備工作&#xff0c;預防意外的事發生。&#xff08;提前做好準備&#xff0c…

ubuntu上 opencv + eclipse + C++

ubuntu上 opencv eclipse C 1. 安裝eclipse 安裝eclipse不用說了&#xff0c;前置條件要安裝java 配置快捷鍵方式 2. 新建c項目 配置opencv環境 project -> properties: 配置c標準庫版本&#xff1a; 配置opencv頭文件&#xff1a; 配置opencv庫文件&#xff1a;…

動態內存管理2+柔性數組

一、動態內存經典筆試題分析 分析錯誤并改正 題目1 void GetMemory(char *p) {p (char *)malloc(100); } void Test(void) {char *str NULL;GetMemory(str);strcpy(str, "hello world");printf(str); } int main() {Test();return 0; }錯誤的原因&#xff1a; …

AI寫PPT可以用嗎?我測試了3款AI寫PPT工具,分享感受

上周五臨下班&#xff0c;領導突然讓我周末趕出一份季度營銷報告 PPT&#xff0c;還要求周一晨會展示。看著空蕩蕩的 PPT 頁面&#xff0c;我滿心都是絕望 —— 周末不僅泡湯&#xff0c;搞不好還得熬夜到凌晨。好在同部門的前輩給我推薦了幾款 AI 寫 PPT 工具&#xff0c;沒想…

PrimeVul論文解讀-如何構建高質量漏洞標簽與數據集

目錄 1. 引入2. 現有漏洞識別方案的不足2.1 數據集中label不準2.2 數據重復2.3 測評標準不夠好 3. 現有漏洞識別數據集分析3.1 關于現有數據集中label的準確率分析3.2 關于現有數據集中數據泄露&#xff08; Data Leakage&#xff09;情況分析 4. 漏洞識別測評5. PrimeVul數據集…