【線程】基于環形隊列的生產者消費者模型

1 環形隊列

環形隊列采用數組來模擬,用取模運算來模擬環狀特性。
在這里插入圖片描述
1.如何判斷環形隊列為空或者為滿?

  • 當環形隊列為時,頭和尾都指向同一個位置。
  • 當環形隊列為滿時,頭和尾也都指向同一個位置。

因此, 可以通過加計數器或者標記位來判滿或者空,也可以預留一個空的位置,作為滿的狀態

1.1 生產者消費者中的環形隊列

2.生產者和消費者什么時候會訪問同一個位置?

1.環形隊列為空的時候,生產者和消費者會訪問同一個位置。
在這里插入圖片描述
環形隊列中,如果隊列為空,那么隊首和隊尾的指針是指向同一個位置的。所以,生產者和消費者也會指向同一個位置。

2.環形隊列為滿的時候,生產者和消費者會訪問同一個位置。
在這里插入圖片描述
環形隊列中,如果隊列為滿,那么隊首和隊尾的指針是指向同一個位置的。所以,生產者和消費者也會指向同一個位置。

其他任何時候,生產者和消費者訪問的都是不同的區域。
在這里插入圖片描述

3.環形隊列中的數據需要注意什么?

1.消費者不能超過生產者。
在這里插入圖片描述
也就是: 不能沒有數據了還繼續消費

2.生產者不能將消費者套圈。
在這里插入圖片描述
也就是: 不能沒有空間了還繼續生產

2 設計信號量

生產者最在意的是空閑的空間個數
消費者最在意的是數據的個數

所以生產者每次在訪問臨界資源之前,需要先申請空間資源的信號量,申請成功就可以進行生產,否則就阻塞等待。

消費者在訪問臨界資源之前,需要申請數據資源的信號量,申請成功就可以消費數據,否則就阻塞等待。

空間資源信號量的申請由生產者進行,歸還(V操作)由消費者進行,表示生產者可以生產數據。

數據資源信號量的申請有消費者進行,歸還(V操作)由生產者進行,表示消費者可以進行消費.

4.空間資源信號如何設計?

最開始,生產者沒有生產,所以空間資源是隊列的大小(滿的)

5.數據資源信號如何設計?

最開始,生產者沒有生產,所以數據資源為0(空的)

2.1 生產者偽代碼

productor_sem = 環形隊列大小;P(productor_sem);//申請空間資源信號量
//申請成功,繼續向下運行。
//申請失敗,阻塞在申請處。.......//從事生產活動——把數據放入隊列中V(comsumer_sem);//歸還數據資源信號量

2.2 消費者偽代碼

comsumer_sem = 0;P(comsumer_sem);//申請數據資源信號量
//申請成功,繼續向下運行。
//申請失敗,阻塞在申請處。.......//從事消費活動——從隊列中消費數據V(proudctor_sem);//歸還空間資源信號量

在環形隊列中,大部分情況下單生產和單消費是可以并發執行的,只有在滿或者空的時候,才會有同步和互斥問題,同步和互斥是通過信號量來實現的。

3 代碼

3.1 RingQueue.h

#include <iostream>
#include <vector>
#include <semaphore.h>
#include <pthread.h>//為什么基于阻塞隊列的生產者消費者模型只需要一個鎖 , 而基于環形隊列的生產者消費者模型需要兩個鎖?
//1.因為阻塞隊列是對同一個隊列的同一個位置進行操作,隊列是共享資源,需要對整個隊列加鎖
//2.阻塞隊列中,生產者和消費者訪問的不是同一個下標,所以兩者是互不干擾的,只需要用條件變量來喚醒阻塞
//但是生產者對生產者而言,是需要加鎖的。消費者對消費者而言,是需要加鎖的。template <class T>
class RingQueue
{static const int defaultnum = 5;
public://p操作,申請信號量,信號量--void p(sem_t& sem){sem_wait(&sem);}//v操作,釋放信號量,信號量++void v(sem_t& sem){sem_post(&sem);}void Lock(pthread_mutex_t& mutex){pthread_mutex_lock(&mutex);}void UnLock(pthread_mutex_t& mutex){pthread_mutex_unlock(&mutex);}public:RingQueue(int cap = defaultnum):_ringqueue(cap),_cap(cap),_c_step(0),_p_step(0){//初始化信號量,給消費者初始的信號量為0,給生產者初始的信號量為cap(滿)//因為最開始的時候沒有商品,無法消費,只能生產sem_init(&_c_data_sem, 0, 0);sem_init(&_p_space_sem, 0, cap);pthread_mutex_init(&_c_mutex, nullptr);pthread_mutex_init(&_p_mutex, nullptr);}//生產商品void push(const T &in){   //1.申請信號量p(_p_space_sem);//2.消費者加鎖Lock(_p_mutex);//3.進行生產_ringqueue[_p_step] = in;_p_step++;_p_step %= _cap;  //保證下標一直都在環形隊列里面//4.解鎖UnLock(_p_mutex);//5.釋放信號量v(_c_data_sem);}void pop(T* out){//1.申請信號量p(_c_data_sem);//2.申請鎖Lock(_c_mutex);//3.消費數據*out = _ringqueue[_c_step];++_c_step;_c_step %= _cap;//4.解鎖UnLock(_c_mutex);//5.生產者信號量++v(_p_space_sem);  //消費完數據之后,生產者的信號量++}
private:std::vector<T> _ringqueue;int _cap;     //capacityint _c_step;  //消費者在環形隊列中的下標int _p_step;  //生產者在環形隊列中的下標sem_t _c_data_sem;  //消費者關注的數據資源sem_t _p_space_sem; //生產者關注的消費資源pthread_mutex_t _c_mutex;   //消費者鎖pthread_mutex_t _p_mutex;   //生產者鎖
};

3.1.1 生產商品

//生產商品void push(const T &in){   //1.申請信號量p(_p_space_sem);//2.消費者加鎖Lock(_p_mutex);//3.進行生產_ringqueue[_p_step] = in;_p_step++;_p_step %= _cap;  //保證下標一直都在環形隊列里面//4.解鎖UnLock(_p_mutex);//5.釋放信號量v(_c_data_sem);}

6.為什么要按照 申請信號量 → 加鎖 → 解鎖 → 釋放信號量 的順序來做?

如果先加鎖再申請信號量,可能會出現以下這種情況:
加鎖之后,發現沒有空閑空間了,于是阻塞。而此時該線程還持有鎖,就會導致死鎖。.
如果先釋放信號量再解鎖,可能會出現以下這種情況:
釋放信號量之后,消費者得知有可以消費的資源,于是開始消費。但是此時并沒有解鎖,因此生產者可能并沒有完全完成生產,但是消費者已經開始消費。就會導致生產消費數據不一致的問題。.

3.1.2 消費商品

void pop(T* out){//1.申請信號量p(_c_data_sem);//2.申請鎖Lock(_c_mutex);//3.消費數據*out = _ringqueue[_c_step];++_c_step;_c_step %= _cap;//4.解鎖UnLock(_c_mutex);//5.生產者信號量++v(_p_space_sem);  //消費完數據之后,生產者的信號量++}

在這里插入圖片描述

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

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

相關文章

二分/雙指針/單調棧隊列專題

1.4924. 矩陣 - AcWing題庫 一開始打表找規律以為是右上角向左下角遞增,但當n很大的時候就不對了,因此我們得去觀察 i * i 100000 * (i - j) j * j i * j 這個式子,我們關心的是這個式子的單調性因此我們可以分別將i和j看作常數來對式子進行求導,可以得到 f(i) 2 * i 10…

Shell $0

個人博客地址&#xff1a;Shell $0 | 一張假鈔的真實世界 我們已經知道在Shell中$0表示Shell腳本的文件名&#xff0c;但在有腳本調用的情形中&#xff0c;子腳本中的$0會是什么值呢&#xff1f;我們通過下面的實例來看。 已測試系統列表&#xff1a; Mac OS X EI Capitan 1…

商品列表及商品詳情展示

前言 本文將展示一段結合 HTML、CSS 和 JavaScript 的代碼&#xff0c;實現了一個簡單的商品展示頁面及商品詳情&#xff0c;涵蓋數據獲取、渲染、搜索及排序等功能。 效果展示 點擊不同的商品會展示對應的商品詳情。 代碼部分 代碼總體實現 <!DOCTYPE html> <htm…

[ VS Code 插件開發 ] 使用 Task ( 任務 ) 代替 createTerminal (終端) 來執行命令

VSCode 官方自己的插件就是這樣執行命令的. 使用體驗 比 默認的終端 好太多了. 重用終端, Shell 集成 , 按任意鍵關閉, 任務是否成功, 左側命令操作 (菜單中功能很多) 等 import * as vscode from vscode; // 執行的命令 let command_str "npm run dev" // 工作目…

大模型綜述一鏡到底(全文八萬字) ——《Large Language Models: A Survey》

論文鏈接&#xff1a;https://arxiv.org/abs/2402.06196 摘要&#xff1a;自2022年11月ChatGPT發布以來&#xff0c;大語言模型&#xff08;LLMs&#xff09;因其在廣泛的自然語言任務上的強大性能而備受關注。正如縮放定律所預測的那樣&#xff0c;大語言模型通過在大量文本數…

Python處理數據庫:MySQL與SQLite詳解

Python處理數據庫&#xff1a;MySQL與SQLite詳解 在數據處理和存儲方面&#xff0c;數據庫扮演著至關重要的角色。Python提供了多種與數據庫交互的方式&#xff0c;其中pymysql庫用于連接和操作MySQL數據庫&#xff0c;而SQLite則是一種輕量級的嵌入式數據庫&#xff0c;Pytho…

【C++】B2124 判斷字符串是否為回文

博客主頁&#xff1a; [小????????] 本文專欄: C 文章目錄 &#x1f4af;前言&#x1f4af;題目描述輸入格式&#xff1a;輸出格式&#xff1a;樣例&#xff1a; &#x1f4af;方法一&#xff1a;我的第一種做法思路代碼實現解析 &#x1f4af;方法二&#xff1a;我…

ubuntuCUDA安裝

系列文章目錄 移動硬盤制作Ubuntu系統盤 前言 根據前篇“移動硬盤制作Ubuntu系統盤”安裝系統后&#xff0c;還不能夠使用顯卡。 如果需要使用顯卡&#xff0c;還需要進行相關驅動的安裝&#xff08;如使用的為Nvidia顯卡&#xff0c;就需要安裝相關的Nvidia顯卡驅動&#xff…

Selenium 使用指南:從入門到精通

Selenium 使用指南&#xff1a;從入門到精通 Selenium 是一個用于自動化 Web 瀏覽器操作的強大工具&#xff0c;廣泛應用于自動化測試和 Web 數據爬取中。本文將帶你從入門到精通地掌握 Selenium&#xff0c;涵蓋其基本操作、常用用法以及一個完整的圖片爬取示例。 1. 環境配…

Sqoop導入MySQL中含有回車換行符的數據

個人博客地址&#xff1a;Sqoop導入MySQL中含有回車換行符的數據 MySQL中的數據如下圖&#xff1a; 檢查HDFS上的目標文件內容可以看出&#xff0c;回車換行符位置的數據被截斷了&#xff0c;導致數據列錯位。 Sqoop提供了配置參數&#xff0c;在導入時丟棄掉數據的分隔符&…

利用matlab尋找矩陣中最大值及其位置

目錄 一、問題描述1.1 max函數用法1.2 MATLAB中 : : :的作用1.3 ind2sub函數用法 二、實現方法2.1 方法一&#xff1a;max和find2.2 方法二&#xff1a;max和ind2sub2.3 方法對比 三、參考文獻 一、問題描述 matlab中求最大值可使用函數max&#xff0c;對于一維向量&#xff0…

PyTorch數據建模

回歸分析 import torch import numpy as np import pandas as pd from torch.utils.data import DataLoader,TensorDataset import time strat = time.perf_counter()

機試題——字符匹配

題目描述 給你一個字符串數組&#xff08;每個字符串均由小寫字母組成&#xff09;和一個字符規律&#xff08;由小寫字母和 . 和 * 組成&#xff09;&#xff0c;識別數組中哪些字符串可以匹配到字符規律上。 . 匹配任意單個字符。* 匹配零個或多個前面的那一個元素。 所謂…

《 C++ 點滴漫談: 二十五 》空指針,隱秘而危險的殺手:程序崩潰的真兇就在你眼前!

摘要 本博客全面解析了 C 中指針與空值的相關知識&#xff0c;從基礎概念到現代 C 的改進展開&#xff0c;涵蓋了空指針的定義、表示方式、使用場景以及常見注意事項。同時&#xff0c;深入探討了 nullptr 的引入及智能指針在提升代碼安全性和簡化內存管理方面的優勢。通過實際…

git push到遠程倉庫時無法推送大文件

一、錯誤 remote: Error: Deny by project hooks setting ‘default’: size of the file ‘scientific_calculator’, is 164 MiB, which has exceeded the limited size (100 MiB) in commit ‘4c91b7e3a04b8034892414d649860bf12416b614’. 二、原因 本地提交過大文件&am…

掌握API和控制點(從Java到JNI接口)_36 JNI開發與NDK 04

4、 *.so的入口函數&#xff1a;JNI_OnLoad() VM (virtual machine)的角色 Java代碼在VM上執行。在執行Java代碼的過程中&#xff0c;如果Java需要與本地代碼(*.so)溝通時&#xff0c; VM就會把*.so視為插件<Tn>而加載到VM里。然后讓Java函數呼叫到這插件<Tn>里的…

Windows圖形界面(GUI)-QT-C/C++ - QT Tab Widget

公開視頻 -> 鏈接點擊跳轉公開課程博客首頁 -> ???鏈接點擊跳轉博客主頁 目錄 一、概述 1.1 什么是 QTabWidget&#xff1f; 1.2 使用場景 二、常見樣式 2.1 選項卡式界面 2.2 動態添加和刪除選項卡 2.3 自定義選項卡標題和圖標 三、屬性設置 3.1 添加頁面&…

[MRCTF2020]Ez_bypass1(md5繞過)

[MRCTF2020]Ez_bypass1(md5繞過) ?? 這道題就是要繞過md5強類型比較&#xff0c;但是本身又不相等&#xff1a; md5無法處理數組&#xff0c;如果傳入的是數組進行md5加密&#xff0c;會直接放回NULL&#xff0c;兩個NuLL相比較會等于true&#xff1b; 所以?id[]1&gg…

90,【6】攻防世界 WEB Web_php_unserialize

進入靶場 進入靶場 <?php // 定義一個名為 Demo 的類 class Demo { // 定義一個私有屬性 $file&#xff0c;默認值為 index.phpprivate $file index.php;// 構造函數&#xff0c;當創建類的實例時會自動調用// 接收一個參數 $file&#xff0c;用于初始化對象的 $file 屬…

Jenkins安裝部署(以及常見報錯解決方案),jdk版本控制器sdkman

目錄 零、環境介紹 一、Jenkins安裝 1、插件安裝以及更換插件源 2、修改jenkins時區 二、sdkman安裝&#xff08;可選&#xff09; 1、sdkman常用方法 2、sdkman常用方法演示 2.1、查看可用的jdk 2.2、下載jdk并切換版本 三、jenkins報錯解決 1、下載sdkman后systemc…