Linux:基于阻塞隊列的生產者消費模型

文章目錄

  • 一、生產者消費者模型的基本原則
    • 💕💕生產者-消費者模型的 321 原則💕💕
  • 二、為何要使用生產者消費者模型
    • 1. 解耦
    • 2. 支持并發 (提高效率)
    • 3. 忙閑不均的支持
  • 三、基于 BlockingQueue 的生產者消費者模型
    • 1. 阻塞隊列的特點
    • 2. C++ 模擬實現
    • 3. 封裝更精細的版本
  • 五、總結


一、生產者消費者模型的基本原則

在多線程編程中,生產者消費者模型是一種常見的并發設計模式。它通過一個**緩沖區(阻塞隊列)**來解耦生產者和消費者,從而解決兩者之間的強耦合問題。生產者只需要負責將數據放入隊列,而消費者只需要負責從隊列中取數據。

這樣設計的好處在于,生產者在完成數據生成后無需等待消費者處理,可以立即返回繼續生成;而消費者也無需關心數據來自哪里,只需要從隊列里取出任務并處理即可。這種方式既保證了系統的高效性,也增強了并發性。

可以把 321 原則用層次化結構來表達,比表格更直觀:


💕💕生產者-消費者模型的 321 原則💕💕

3 個關系

  1. 生產者之間互斥:多個生產者不能同時寫入隊列,否則會破壞數據一致性。
  2. 消費者之間互斥:多個消費者不能同時讀取同一數據,否則會出現重復消費。
  3. 生產者與消費者之間互斥與同步:隊列為空時消費者等待,隊列滿時生產者等待,二者既互斥又必須協同。

2 個角色

  1. 生產者:負責不斷產生數據并放入隊列。
  2. 消費者:負責從隊列中取出數據并進行處理。

1 個交易場所

阻塞隊列 / 環形隊列:作為共享緩沖區,承載生產與消費的銜接。


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

1. 解耦

生產者和消費者之間沒有直接依賴關系,它們通過阻塞隊列完成數據交互,降低了耦合度。


2. 支持并發 (提高效率)

生產者和消費者可以并發執行,充分利用 CPU 資源。這里提高效率并不是說生產者消費模型可以更快的派發任務,而是通過一個串行的交易場所,可以將任務派發給不同的線程,也可以由不同的線程同時生產,在生產者和消費者自己之間可以做到并發執行,因此提高了效率,畢竟將來派發任務只占有一點點的份額,執行任務才是大頭。


3. 忙閑不均的支持

如果生產者生成速度快于消費者處理速度,多余的數據會存放在阻塞隊列中;反之,消費者可以在數據不足時自動等待。這樣有效平衡了生產和消費之間的速度差異。


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

1. 阻塞隊列的特點

在普通隊列中,如果取數據時隊列為空會直接返回錯誤,而阻塞隊列則會讓取數據的線程阻塞等待直到有數據為止;同樣地,如果存放數據時隊列已滿,線程也會被阻塞,直到有空余位置。這種機制天生適合生產者消費者模型。

在這里插入圖片描述

在這里插入圖片描述


2. C++ 模擬實現

BlockQueue.hpp

// 阻塞隊列的實現
#pragma once#include <iostream>
#include <string>
#include <queue>
#include <pthread.h>const int defaultcap = 5; // for testtemplate <typename T>
class BlockQueue
{
private:bool IsFull() { return _q.size() >= _cap; }bool IsEmpty() { return _q.empty(); }public:BlockQueue(int cap = defaultcap) : _cap(cap), _csleep_num(0), _psleep_num(0){pthread_mutex_init(&_mutex, NULL);pthread_cond_init(&_full_cond, NULL);pthread_cond_init(&_empty_cond, NULL);}void Equeue(const T &in){pthread_mutex_lock(&_mutex);while (IsFull()){_psleep_num++;std::cout << "生產者,進入休眠了: _psleep_num" << _psleep_num << std::endl;pthread_cond_wait(&_full_cond, &_mutex);_psleep_num--;}// 走到這里一定有空間了_q.push(in);if (_csleep_num > 0){pthread_cond_signal(&_empty_cond);std::cout << "喚醒消費者..." << std::endl;}pthread_mutex_unlock(&_mutex);}T Pop(){// 消費者調用pthread_mutex_lock(&_mutex);while (IsEmpty()){_csleep_num++;pthread_cond_wait(&_empty_cond, &_mutex);_csleep_num--;}T data = _q.front();_q.pop();if (_psleep_num > 0){pthread_cond_signal(&_full_cond);std::cout << "喚醒消費者" << std::endl;}// pthread_cond_signal(&_full_cond);pthread_mutex_unlock(&_mutex);return data;}~BlockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_full_cond);pthread_cond_destroy(&_empty_cond);}private:std::queue<T> _q;int _cap;pthread_mutex_t _mutex;pthread_cond_t _full_cond;pthread_cond_t _empty_cond;int _csleep_num; // 消費者休眠的個數int _psleep_num; // 生產者休眠的個數
};

3. 封裝更精細的版本

Cond.hpp

// 阻塞隊列的實現
#pragma once#include <iostream>
#include <string>
#include <queue>
#include "Mutex.hpp"
#include "Cond.hpp"const int defaultcap = 10; // for testusing namespace MutexModule;
using namespace CondModule;template <typename T>
class BlockQueue
{
private:bool IsFull() { return _q.size() >= _cap; }bool IsEmpty() { return _q.empty(); }public:BlockQueue(int cap = defaultcap): _cap(cap), _csleep_num(0), _psleep_num(0){}void Equeue(const T &in){{LockGuard lockguard(_mutex);// 生產者調用while (IsFull()){// 應該讓生產者線程進行等待// 重點1:pthread_cond_wait調用成功,掛起當前線程之前,要先自動釋放鎖!!// 重點2:當線程被喚醒的時候,默認就在臨界區內喚醒!要從pthread_cond_wait// 成功返回,需要當前線程,重新申請_mutex鎖!!!// 重點3:如果我被喚醒,但是申請鎖失敗了??我就會在鎖上阻塞等待!!!_psleep_num++;std::cout << "生產者,進入休眠了: _psleep_num" << _psleep_num << std::endl;// 問題1: pthread_cond_wait是函數嗎?有沒有可能失敗?pthread_cond_wait立即返回了// 問題2:pthread_cond_wait可能會因為,條件其實不滿足,pthread_cond_wait 偽喚醒_full_cond.Wait(_mutex);_psleep_num--;}// 100%確定:隊列有空間_q.push(in);// 臨時方案// v2if (_csleep_num > 0){_empty_cond.Signal();std::cout << "喚醒消費者..." << std::endl;}}}T Pop(){T data;{// 消費者調用LockGuard lockguard(_mutex);while (IsEmpty()){_csleep_num++;_empty_cond.Wait(_mutex);_csleep_num--;}data = _q.front();_q.pop();if (_psleep_num > 0){_full_cond.Signal();std::cout << "喚醒生產者" << std::endl;}}return data;}~BlockQueue(){}private:std::queue<T> _q; // 臨界資源!!!int _cap;         // 容量大小Mutex _mutex;Cond _full_cond;Cond _empty_cond;int _csleep_num; // 消費者休眠的個數int _psleep_num; // 生產者休眠的個數
};

五、總結

生產者消費者模型是一種經典的多線程設計模式。它利用阻塞隊列實現生產與消費的解耦,不僅提高了系統的并發能力,還能平衡兩者之間的處理速度差異。在 C++ 中,我們可以通過 queue + pthread + 條件變量 實現一個簡易的阻塞隊列,再結合任務封裝,實現靈活的生產者消費者模型。

未來在實際工程中,如果使用 C++11 及以上版本,可以考慮用 std::mutexstd::condition_variable 替代 pthread,寫法會更簡潔。


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

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

相關文章

ensp啟動路由器報錯40

1. 先關閉 eNSP 模擬器、關閉 Virtualbox2. 在everything里面搜索 .VirtualBox文件夾&#xff0c;然后刪掉3. 再打開 eNSP&#xff0c;不添加任何模擬設備&#xff0c;單擊“菜單-工具-注冊設備”&#xff0c;將 AR_Base 重新注冊。4. 關閉 eNSP 模擬器

代碼隨想錄二刷之“圖論”~GO

A.深搜與廣搜&#xff08;重點掌握&#xff01;&#xff01;&#xff01;&#xff01;&#xff09; 深搜類似于回溯法 搜索方向&#xff0c;是認準一個方向搜&#xff0c;直到碰壁之后再換方向換方向是撤銷原路徑&#xff0c;改為節點鏈接的下一個路徑&#xff0c;回溯的過程…

基于Echarts+HTML5可視化數據大屏展示-白茶大數據溯源平臺V2

效果展示&#xff1a;代碼結構&#xff1a;主要代碼實現 index.html布局 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta n…

Linux 系統網絡配置及 IP 地址相關知識匯總

Linux 系統網絡配置及 IP 地址相關知識匯總 一、IP地址基礎 IP地址&#xff1a;在計算機網絡中用來唯一標識一臺設備的一組數字。 二、IPv4相關知識 1. IPv4的表示方法 采用點分十進制表示&#xff0c;即由4個0-255的十進制數通過點分隔組成&#xff08;如192.168.1.1&#xff…

百度股價突破120美元創年內新高,AI云成為增長新引擎

美東時間9月16日&#xff0c;百度&#xff08;NASDAQ: BIDU&#xff09;美股大漲近8%&#xff0c;收盤價突破120美元&#xff0c;站上124美元高位&#xff0c;創2023年10月以來新高。北京時間9月17日港股開盤&#xff0c;百度&#xff08;09888.HK&#xff09;港股再次暴漲&…

《彩虹六號:圍攻》“Siege X”發布會3月14日舉行!

使用jQuery的常用方法與返回值分析 jQuery是一個輕量級的JavaScript庫&#xff0c;旨在簡化HTML文檔遍歷和操作、事件處理以及動畫效果的創建。本文將介紹一些常用的jQuery方法及其返回值&#xff0c;幫助開發者更好地理解和運用這一強大的庫。 1. 選擇器方法 jQuery提供了多種…

[從青銅到王者] Spring Boot+Redis+Kafka電商場景面試全解析

互聯網大廠Java開發崗技術面試實錄&#xff1a;嚴肅面試官VS搞笑程序員謝飛機 文章內容 第一輪&#xff1a;基礎框架與并發控制&#xff08;電商系統基礎能力&#xff09; 面試官&#xff08;嚴肅&#xff09;&#xff1a;歡迎進入面試環節&#xff0c;首先請用3句話總結Spring…

【DMA】DMA架構解析

目錄 1 DMA架構 1. 芯片架構圖一覽 2. AHB總線矩陣掛載 3. AHB1/APB1的橋和AHB1/APB2的橋 4. DMA1 和 DMA2 的區別 2 AHB總線矩陣 1 DMA架構 1. 芯片架構圖一覽 2. AHB總線矩陣掛載 stm32F411 芯片的 AHB 總線矩陣上共掛載了 6 主 5 從 六主&#xff1a; Icode-bus、D…

GPS 定位器:精準追蹤的“隱形守護者”

GPS 定位器&#xff1a;精準追蹤的“隱形守護者” 一、什么是 GPS 定位器&#xff1f; GPS 定位器是一種基于 全球定位系統&#xff08;Global Positioning System, GPS&#xff09; 的智能追蹤設備。 通過接收衛星信號并結合通信模塊&#xff08;如 4G、NB-IoT&#xff09;&am…

前端拖拽排序實現

1. 使用 HTML5 事件 觸發時機 核心任務 dragstart 開始拖拽時 準備數據&#xff0c;貼上標簽 dragover 經過目標上方時 必須 preventDefault()&#xff0c;發出“允許放置”的信號 dragleave 離開目標上方時 清理高亮等臨時視覺效果 drop 在目標上松手時 接收數據…

arm coresight

這是一個arm設計的調試基礎架構&#xff0c;我們常用的debug基本都包含在內。比如ETM、PTM、ITM、HTM、ETB等。 注意ETM、PTM、ITM、HTM、ETB是coresight的子集。這些工具相比普通debug的斷點調試&#xff0c;需要更高的專業水平&#xff0c;因此也用于復雜軟件故障定位、性能…

《華為基本法》 —— 企業發展的導航儀

當一家企業從 “小作坊” 向 “規模化組織” 跨越時&#xff0c;最需要的是什么&#xff1f;華為的答案&#xff0c;藏在 1998 年出臺的《華為基本法》里。1998 年&#xff0c;《華為基本法》正式頒布&#xff0c;這部凝結華為早期經營智慧的綱領性文件&#xff0c;不僅為華為從…

【完整源碼+數據集+部署教程】傳統韓文化元素分割系統: yolov8-seg-GFPN

背景意義 研究背景與意義 隨著全球化的加速&#xff0c;傳統文化的保護與傳承面臨著前所未有的挑戰。尤其是韓國的傳統文化&#xff0c;作為東亞文化的重要組成部分&#xff0c;蘊含著豐富的歷史、藝術和哲學內涵。然而&#xff0c;隨著現代化進程的推進&#xff0c;許多傳統文…

構建AI智能體:三十五、決策樹的核心機制(一):刨根問底鳶尾花分類中的參數推理計算

一、初識決策樹想象一個生活中的場景&#xff0c;我們去水果店買一個西瓜&#xff0c;該怎么判斷一個西瓜是不是又甜又好的呢&#xff1f;我們可能會問自己一系列問題&#xff1a;首先看看它的紋路清晰嗎&#xff1f;如果“是”&#xff0c;那么它可能是個好瓜。如果“否“&…

c語言中實現線程同步的操作

線程 常見問題 同步權限 在多線程 / 多進程并發時&#xff0c;為避免共享資源&#xff08;如內存變量、硬件設備、文件&#xff09;被同時修改導致的數據不一致&#xff0c;需要通過 “同步機制” 控制誰能訪問資源 ——“獲取同步權限” 就是線程 / 進程申請這種訪問資格的過程…

一臺設備管理多個 GitHub 賬號:從配置到切換的完整指南

一臺設備管理多個 GitHub 賬號&#xff1a;從配置到切換的完整指南 在日常開發中&#xff0c;我們經常需要在同一臺電腦上使用多個 GitHub 賬號&#xff08;比如個人賬號和工作賬號&#xff09;。但默認情況下&#xff0c;Git 會優先使用全局配置的賬號&#xff0c;導致推送代…

即插即用,秒入虛擬:TouchDIVER Pro 觸覺手套 賦能 AR/VR 高效交互

一、即插即用&#xff0c;零門檻開啟沉浸之旅 在XR&#xff08;擴展現實&#xff09;技術高速發展的今天&#xff0c;用戶對“真實感”的追求愈發迫切。Weart公司旗下旗艦產品TouchDIVER Pro觸覺手套&#xff0c;憑借無需適配器、無需復雜設置的極簡設計&#xff0c;打破傳統觸…

GitHub熱榜項目 - 日榜之應用場景與未來發展趨勢

一、引言GitHub熱榜項目 - 日榜呈現出豐富多樣的技術成果&#xff0c;這些項目蘊含著巨大的應用潛力&#xff0c;并且對未來數智化技術的發展有著重要的指示作用。深入探究其應用場景以及未來發展趨勢&#xff0c;能讓我們更好地把握技術發展方向&#xff0c;將這些前沿技術應用…

Linux網絡:socket編程TCP

文章目錄前言一&#xff0c;服務器端流程1-1 綁定協議1-2 綁定IP和端口1-3 監聽客戶端1-4 接收連接1-5 收發數據1-6 關閉連接1-7 服務端整體代碼二&#xff0c;客戶端流程2-1 指定地址和端口2-2 連接服務器2-3 發送消息2-4 客戶端整體代碼前言 TCP 的通信過程就像兩個人打電話…

飛書智能查詢機器人搭建說明文檔

飛書智能查詢機器人搭建說明文檔 一、使用手冊 1. 創建飛書機器人應用 如果僅需對接已有機器人應用則可跳過該步驟&#xff08;建議各業務部門獨立使用各自的機器人應用&#xff09;。在飛書開發者后臺中創建企業自建應用&#xff0c;添加機器人應用能力并申請對應的身份權限…