Linux學習:基于環形隊列的生產者消費者模型

目錄

  • 1. 環形隊列的概念與實現方法
    • 1.1 環形隊列的概念
    • 1.2 環形隊列的一般實現方法
  • 2. 多線程相關的信號量概念與接口
    • 2.1 信號量類型
    • 2.2 信號量的初始化與銷毀
    • 2.3 信號量的P申請、V釋放操作
  • 3. 基于環形隊列實現p、c模型的設計方案
    • 3.1 環形隊列(ringqueue)作為共享資源緩沖區的設計要求
    • 3.2 環形隊列作為共享資源緩沖區的實現方法
  • 4. 代碼實現
    • 4.1 線程類
    • 4.2 線程執行的任務
    • 4.3 RingQueue類
    • 4.4 主干代碼

1. 環形隊列的概念與實現方法

1.1 環形隊列的概念

  • 環形隊列:
    ??容量固定,首尾相連的一個隊列(遵循先進先出),于邏輯上形狀為一個環形。當存儲數據超過容量上限后,再次給環形隊列插入元素時,不會產生越界的情況。新的數據會回繞到數據首部進行插入,將空間中的舊數據覆蓋
    在這里插入圖片描述

1.2 環形隊列的一般實現方法

??最常用于實現環形隊列的數據結構為順序表,其通過對下標的取模操作,就可以很好的實現存儲空間邏輯上的環形要求,具體實現方案如下:


方案1:

  1. 開辟要求容量cap大小的順序表

  2. 以變量begin記錄隊列中首個數據的下標,起始設置為0

  3. 以變量end記錄隊列中新數據插入位置的下標,起始設置為0
    在這里插入圖片描述

  4. 以變量count記錄隊列中存儲數據的個數,每次插入數據后進行++,每次刪除數據后進行--。隊列為空與為滿時,beginend的位置相同,因此,需要以count中記錄的值來做區分

  5. 插入元素時,向end記錄的下標位置寫入數據,然后再++

  6. 刪除元素時,只需讓begin進行++即可,beginend下標之間的空間中存儲著隊列中的數據

  7. beginend每次++后都要對其進行取模操作%= cap

方案2:
??方案2的大體實現思路與方案1相同,只是于隊列空滿判定實現上有所區別。此種方法,會將隊列額外多開辟出一塊空間,隊列容量為cap,實際大小為cap + 1。此塊多出來的空間不用來存放數據,而是用來標識環形隊列的滿狀態(end + 1) % (cap + 1) == begin
在這里插入圖片描述

2. 多線程相關的信號量概念與接口

2.1 信號量類型

#include <semaphore.h>
//原生線程庫中的內容,編譯時帶-lpthread選項
sem_t sem;//同樣為計數器

2.2 信號量的初始化與銷毀

信號量的初始化:

int sem_init(sem_t *sem, int pshared, unsigned int value);
  • 參數1sem_t* 傳入需要初始化的信號量地址
  • 參數2int pshared 用于父子進程之間共享設置為1,用于多線程之間共享設置為0
  • 參數3unsigned int value 用于設置信號量的大小
  • 返回值int 成功時,返回0;失敗時,返回-1,并將錯誤碼寫入errno

信號量的銷毀:

int sem_destroy(sem_t* sem);
  • 參數sem_t* sem 傳入需要銷毀的信號量的地址
  • 返回值int 成功時,返回0;失敗時,返回-1,并將錯誤碼寫入errno

2.3 信號量的P申請、V釋放操作

信號量的P操作:

int sem_wait(sem_t* sem);
  • 參數sem_t* sem 傳入需要進行P操作的信號量地址
  • 返回值int 成功時,返回0;失敗時,返回-1,并將錯誤碼寫入errno

信號量的V操作:

int sem_post(sem_t* sem);
  • 參數sem_t* sem 傳入需要進行V操作的信號量地址
  • 返回值int 成功時,返回0;失敗時,返回-1,并將錯誤碼寫入errno

??信號量不僅僅其申請與釋放操作都是互斥的,而且本身還同時具有計數的作用。因此,在功能上,其就相當于是互斥鎖與條件變量的結合,但其又只需要一條操作語句就可以實現互斥鎖與條件變量的配合效果。所以,在代碼編寫角度,信號量比互斥鎖 + 條件變量更簡單也更簡潔。

3. 基于環形隊列實現p、c模型的設計方案

3.1 環形隊列(ringqueue)作為共享資源緩沖區的設計要求

在這里插入圖片描述
??生產者生產數據存入隊列,消費者消費數據從隊列中獲取,此處使用方案1中的環形隊列設計方式。

  • 生產者與消費者訪問共享資源時,需要保持互斥且同步
  • 環形隊列中有多塊空間,只有隊列為滿或為空時,生產者與消費者才會訪問到同一塊空間,即訪問共享資源。因此,除開隊列為空為滿的情況之外,其他場景中,消費者與生產者都可以并發地訪問環形隊列

3.2 環形隊列作為共享資源緩沖區的實現方法

在這里插入圖片描述

  • 對于生產者而言,其需要向隊列中插入數據,所以,隊列中空余的空間(room)是其所需的資源
  • 對于消費者而言,其需要從隊列中獲取數據,所以,隊列空間中存儲的數據(data)是其所需的資源

在這里插入圖片描述
??定義兩個信號量room_semdata_sem,分別作為生產者資源與消費者資源的計數器,每次在生產者、消費者線程訪問環形隊列之前,都要預先申請對應的信號量資源。變量productor_index記錄生產者生產資源的放置位置,變量consumer_index記錄消費者從環形隊列中獲取數據的位置。生產者之間、消費者之間它們對于環形隊列的訪問需要保持互斥性,此處實現可以使用一把鎖來實現,但這樣生產者、消費者之間大多數情況下的并發就會被影響,因此,我們定義分別定義兩把鎖,來分別實現同類之間的互斥,彼此之間并發。


  • 環形隊列作為共享資源緩沖區的優勢
    ??隊列中擁有多塊空間,生產者與消費者線程大多數情況下都不會訪問同一塊資源,因此,可以保證它們之間的并發訪問,提高程序的效率。

4. 代碼實現

4.1 線程類

namespace ThreadModule
{template<typename T>using func_t = function<void(T&, string)>;template<typename T>class Thread{public:Thread(func_t<T> func, T& data, string name = "none-thread"):_func(func), _data(data), _name(name), _stop(true){}~Thread(){}void Execute(){_func(_data, _name);}static void* threadroutine(void* arg){Thread<T>* ptd = static_cast<Thread<T>*>(arg);ptd->Execute();return nullptr;}bool start(){int n = pthread_create(&_tid, nullptr, threadroutine, this);if(n){return false; }_stop = false;return true;}void join(){if(!_stop){pthread_join(_tid, nullptr);}}void detach(){if(!_stop){pthread_detach(_tid);}}string name(){return _name;}void stop(){_stop = true;}private:pthread_t _tid;string _name;func_t<T> _func;T& _data;bool _stop;};}

4.2 線程執行的任務

using task_t = function<void()>;void download()
{cout << "task is download" << endl;
}

4.3 RingQueue類

#ifndef RING_QUEUE
#define RING_QUEUE
#include <vector>
#include <semaphore.h>
#include <pthread.h>template<typename T>
class RingQueue
{
private:void P(sem_t* sem){sem_wait(sem);//wait申請}void V(sem_t* sem){sem_post(sem);//post單詞譯為放入,post操作為釋放}void Lock(pthread_mutex_t* lock){pthread_mutex_lock(lock);}void Unlock(pthread_mutex_t* lock){pthread_mutex_unlock(lock);}public:RingQueue(int cap):_cap(cap), _ringbuffer(cap), _productor_index(0), _consumer_index(0){sem_init(&_room_sem, 0, _cap);//父子進程之間共享pshared: 1, 多線程之間共享pshared: 0sem_init(&_data_sem, 0, 0);pthread_mutex_init(&_productor_lock, nullptr);pthread_mutex_init(&_consumer_lock, nullptr);}void Enqueue(const T& data){P(&_room_sem);Lock(&_productor_lock);_ringbuffer[_productor_index++] = data;_productor_index %= _cap;Unlock(&_productor_lock);V(&_data_sem);}void Pop(T* data){P(&_data_sem);Lock(&_consumer_lock);*data = _ringbuffer[_consumer_index++];_consumer_index %= _cap;Unlock(&_consumer_lock);V(&_room_sem);}~RingQueue(){sem_destroy(&_room_sem);sem_destroy(&_data_sem);pthread_mutex_destroy(&_productor_lock);pthread_mutex_destroy(&_consumer_lock);}private:vector<T> _ringbuffer;int _cap;int _productor_index;int _consumer_index;sem_t _room_sem;sem_t _data_sem;pthread_mutex_t _productor_lock;pthread_mutex_t _consumer_lock;
};#endif

4.4 主干代碼

在這里插入圖片描述

using ringqueue_t = RingQueue<task_t>;void ProductorRun(ringqueue_t& rq, string name)
{while(true){rq.Enqueue(download);cout << name << " is producted" << endl;}
}void ConsumerRun(ringqueue_t& rq, string name)
{while(true){sleep(1);task_t task;rq.Pop(&task);cout << name << " get : "; task();//bad_function_call: 嘗試調用沒有目標的function對象時會出現此異常}
}void InitComm(vector<Thread<ringqueue_t>>& threads, ringqueue_t& rq, int num, func_t<ringqueue_t> func, string who)
{for(int i = 0; i < num; i++){string name = who + '-' + to_string(i + 1);threads.emplace_back(func, rq, name);}
}void InitProductor(vector<Thread<ringqueue_t>>& threads, ringqueue_t& rq, int num)
{InitComm(threads, rq, num, ProductorRun, "productor");
}void InitConsumer(vector<Thread<ringqueue_t>>& threads, ringqueue_t& rq, int num)
{InitComm(threads, rq, num, ConsumerRun, "consumer");
}void StartAll(vector<Thread<ringqueue_t>>& threads)//vector必須用引用,存儲線程tid
{for(auto& thread : threads){thread.start();}
}void WaitAllThread(vector<Thread<ringqueue_t>> threads)//嘗試用拷貝對象是否能夠正常回收進程
{for(auto thread : threads){thread.join();}
}int main()
{ringqueue_t rq(5);vector<Thread<ringqueue_t>> threads;InitProductor(threads, rq, 3);InitConsumer(threads, rq, 2);StartAll(threads);WaitAllThread(threads);return 0;
}

??此處設計時,將創建線程類與真正創建并啟動線程分開執行。這是因為直接以threads.back().start()創建后調用啟動線程,其中threads.back()迭代器可能會由于不斷向threads中插入新的線程類對象,導致發生擴容,最終使得原迭代器失效

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

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

相關文章

【左程云算法07】隊列和棧-鏈表數組實現

目錄 ?編輯1&#xff09;隊列的介紹 核心操作 3&#xff09;隊列的鏈表實現和數組實現 使用數組實現隊列 2&#xff09;棧的介紹 核心操作 4&#xff09;棧的數組實現 使用語言內置的實現 使用數組手動實現棧 5&#xff09;環形隊列的實現 leecode622 代碼解析 視頻…

Docker 清理完整指南:釋放磁盤空間的最佳實踐

前言 隨著 Docker 使用時間的增長,系統中會積累大量的容器、鏡像、數據卷和構建緩存,占用大量磁盤空間。本文將詳細介紹如何有效清理 Docker 資源,釋放磁盤空間,保持系統整潔。 Docker 資源類型 Docker 主要占用磁盤空間的資源包括: 容器 (Containers):運行中和已停止…

零基礎快速了解掌握Linux防火墻-Iptables

一、 Iptables概述Iptables 是一個用戶空間程序&#xff0c;可以用于設置和管理 Linux 操作系統的內核級防火墻。它通過表、鏈和 規則組成&#xff0c;可以靈活地根據不同的需求進行配置。iptables 具有以下特點&#xff1a;Iptables 作為內核級別的防火墻&#xff0c;具有高效…

12公里無人機圖傳模組:從模糊到超高清的飛躍,抗干擾能力全面升級

在無人機行業飛速發展的今天&#xff0c;高清圖像傳輸已成為衡量無人機性能的重要標志之一。過去&#xff0c;無人機在長距離飛行時常常面臨信號衰減、圖像模糊&#xff0c;甚至數據丟失的問題&#xff0c;影響了用戶的體驗與應用效果。為了打破這一瓶頸&#xff0c;業內專家不…

從 “模板” 到 “場景”,用 C++ 磨透拓撲排序的實戰邏輯

文章目錄前言&#xff1a;《算法磨劍: 用C思考的藝術》 專欄《C&#xff1a;從代碼到機器》 專欄《Linux系統探幽&#xff1a;從入門到內核》 專欄正文&#xff1a;[B3644 【模板】拓撲排序 / 家譜樹](https://www.luogu.com.cn/problem/B3644)【解法】【參考代碼】[P2712 攝像…

盲盒抽卡機小程序:從0到1的蛻變之路

盲盒抽卡機小程序從概念提出到最終上線&#xff0c;經歷了從0到1的蛻變過程。這個過程充滿了挑戰與機遇&#xff0c;也凝聚了開發團隊的智慧和汗水。本文將分享盲盒抽卡機小程序的開發歷程&#xff0c;探討其背后的技術實現和市場策略。需求分析&#xff1a;明確目標用戶與市場…

分層-三層架構

文章目錄介紹代碼拆分Dao層server層controller層運行結果介紹 在我們進行程序設計以及程序開發時&#xff0c;盡可能讓每一個接口、類、方法的職責更單一些&#xff08;單一職責原則&#xff09;。 單一職責原則&#xff1a;一個類或一個方法&#xff0c;就只做一件事情&#…

Vue2 VS Vue3

vue3 是的&#xff0c;Vue 3 確實取消了基于 JavaScript 原型的 Vue 和 VueComponent 構造函數&#xff08;即你提到的 vm 和 vc&#xff09;&#xff0c;取而代之的是一種完全不同的、基于普通對象和代理&#xff08;Proxy&#xff09;的實例管理方式。 這是一個顛覆性的改變…

Vue3入門到實戰,最新版vue3+TypeScript前端開發教程,Vue3簡介,筆記02

筆記02 一、Vue3簡介 1.1、Vue3發布日期&#xff1a; 2020年9月18日 1.2、Vue3做了哪些升級&#xff1a; 1.2.1、性能的提升 官方發版地址&#xff1a;Release v3.0.0 One Piece vuejs/core 打包大小減少41%初次渲染快55%更新渲染快133%內容減少54% 1.2.2、源碼的優化…

.net core webapi/mvc阿里云服務器部署 - 錯誤解決

錯誤及解決方案缺少web.config配置HTTP 錯誤 500.19 - Internal Server Error檢查 IIS 配置1. 確保 .NET Core Hosting Bundle 已安裝2. 檢查 應用程序池 配置3. 檢查 IIS MIME 類型檢查文件權限1. 確保 IIS 用戶 有權限訪問網站目錄2. 檢查 web.config 文件權限啟用詳細錯誤日…

多輸入(input)多輸出(output)驗證

#作者&#xff1a;程宏斌 文章目錄前言Flb 1.9.4 INCLUDE配置測試測試方案測試配置文件測試命令Flb 3.0.2 INCLUDE配置測試測試方案測試配置文件啟動命令結論結論一&#xff1a;結論二&#xff1a;前言 需要設計并執行一組測試用例&#xff0c;這些測試用例將包括以子文件形式…

行業學習【電商】:垂直電商如何理解?以專業寵物平臺為例

聲明&#xff1a;以下部分內容含AI生成 “寵物等愛好者的專業平臺”指的是垂直電商的一個具體例子。 “垂直電商” 就是指不賣所有東西&#xff0c;只深耕某一個特定領域&#xff08;即“垂直”領域&#xff09;的電商平臺。 “寵物愛好者的專業平臺”就是這樣一個專門為養寵…

GPT(Generative Pre-trained Transformer)模型架構與損失函數介紹

目錄 一、核心架構&#xff1a;Transformer Decoder 1. 核心組件&#xff1a;僅解碼器&#xff08;Decoder-Only&#xff09;的堆疊 2. 輸入表示&#xff1a;Token 位置 3. 輸出 二、訓練過程&#xff1a;兩階段范式 階段一&#xff1a;預訓練&#xff08;Pre-training&…

GitHub 熱榜項目 - 日榜(2025-09-10)

GitHub 熱榜項目 - 日榜(2025-09-10) 生成于&#xff1a;2025-09-10 統計摘要 共發現熱門項目&#xff1a;15 個 榜單類型&#xff1a;日榜 本期熱點趨勢總結 本期GitHub熱榜呈現三大技術熱點&#xff1a;LLM智能體應用爆發&#xff08;如parlant、AutoAgent&#xff09;&a…

論文閱讀:arxiv 2023 Large Language Models are Not Stable Recommender Systems

總目錄 大模型相關研究&#xff1a;https://blog.csdn.net/WhiffeYF/article/details/142132328 https://arxiv.org/pdf/2312.15746 速覽 破解大語言模型在推薦系統中的不穩定性 該論文聚焦于大語言模型&#xff08;LLMs&#xff09;在推薦系統中的應用問題&#xff0c;指出…

Linux的使用——FinalShell下載使用及連接云服務器的教程

一、注冊免費阿里云服務器 1. 進入阿里云服務器官網 阿里云-計算&#xff0c;為了無法計算的價值https://www.aliyun.com/?spm5176.ecscore_server.console-base_top-nav.dlogo.39144df5uvPLOm 2. 點擊免費試用 這里我已經試用過了&#xff0c;大家選擇合適的云服務器點擊立…

如何清理 Docker 占用的巨大磁盤空間

我相信很多人在使用 Docker 一段時間后&#xff0c;都會遇到一個常見問題&#xff1a;磁盤空間被迅速吃光&#xff0c;尤其是在進行頻繁的鏡像構建、測試和運行容器時。以我自己為例&#xff0c;在 Ubuntu 24.04設備上&#xff0c;docker system df -v 一看&#xff0c;Docker …

【CMake】緩存變量

目錄 一. 緩存變量 二.創建緩存變量 2.1.使用set()來創建緩存變量 2.2.使用FORCE參數來覆蓋緩存變量 2.2.1.示例1——不帶force的set是不能覆蓋已經存在的緩存變量的 2.2.2.示例2——帶force的set才能覆蓋已經存在的緩存變量 2.2.3.對比示例 2.3.命令行 -D 創建/覆蓋緩…

vue2使用若依框架動態新增tab頁并存儲之前的tab頁的操作

1. 應用場景&#xff1a;點擊歷史記錄&#xff0c;要比較兩個tab頁的內容時&#xff0c;需要做到切換tab頁來回看左右對數據對比。2.開發難點若依項目正常是把路由配置到菜單管理里&#xff0c;都是設定好的。不過它也給我們寫好了動態新增tab頁的方&#xff0c;需要我們自己來…

論文閱讀-SelectiveStereo

文章目錄1 概述2 模塊2.1 SelectiveIGEV和IGEV的差異2.2 上下文空間注意力2.2.1 通道注意力2.2.2 空間注意力2.3 SRU3 效果參考資料1 概述 本文主要結合代碼對Selective的創新點進行針對性講解&#xff0c;相關的背景知識可以參考我寫的另兩篇文章論文閱讀-RaftStereo和論文閱…