【Linux】POSIX信號量和基于環形隊列的生產消費者模型

目錄

寫在前面的話

什么是POSIX信號量

POSIX信號量的使用

基于環形隊列的生產消費者模型


?

寫在前面的話

? ? ? ? 本文章主要先介紹POSIX信號量,以及一些接口的使用,然后再編碼設計一個基于環形隊列的生產消費者模型來使用這些接口。

? ? ? ? 講解POSIX信號量時,首先需要對信號量有一定的了解,大家可以去看我的這一篇文章:systemV信號量,文章的前面我詳細的說明了什么是信號量以及對它的理解。


什么是POSIX信號量

????????POSIX信號量(POSIX semaphore)是一種線程同步機制,用于管理共享資源的并發訪問。POSIX信號量是基于POSIX標準定義的一組函數和數據類型,旨在提供跨平臺的線程同步能力。

????????POSIX信號量允許線程在訪問共享資源之前獲取一個信號量,通過增加或減少信號量值來控制對資源的訪問。當信號量值大于零時,線程可以獲取資源并繼續執行。當信號量值為零時,線程將被阻塞,直到其他線程釋放資源并增加信號量的值。這樣可以有效地實現對共享資源的互斥訪問和線程之間的同步。


POSIX信號量的使用

  • sem_init()用于初始化一個信號量對象。

函數原型如下:

int sem_init(sem_t *sem, int pshared, unsigned int value);
  • pshared:0表示線程間共享,非零表示進程間共享
  • value:信號量初始值
  • sem_destroy()用于銷毀一個信號量對象。

函數原型如下:

int sem_destroy(sem_t *sem)
  • sem_wait()嘗試獲取一個信號量,如果信號量值大于零,則將其減少并繼續執行;否則,線程將被阻塞。

函數原型如下:

int sem_wait(sem_t *sem); //P()
  • sem_post():釋放一個信號量,將其值增加,喚醒可能正在等待該信號量的線程。

函數原型如下:

int sem_post(sem_t *sem);//V()
  • sem_trywait():與?sem_wait()?類似,但是嘗試獲取信號量時不阻塞線程,而是立即返回。
  • sem_timedwait():與?sem_wait()?類似,但是可以設置超時時間,如果在超時時間內仍無法獲取信號量,則返回錯誤。

以上兩個函數了解即可.


基于環形隊列的生產消費者模型

????????這個相當于改變了交易場所,由阻塞隊列變成了 環形隊列.

下面是利用環形隊列的幾種策略:

  • 環形隊列采用數組模擬,用模運算來模擬環狀特性

  • 但是上面的設計有一個問題,假設head生產,tail消費;就是當head和tail重合的時候,我們不知道到底是head == tail還是tail == head,?即不知道此時環形隊列為空還是為滿。所以可以通過加計數器或者標記位來判斷滿或者空。另外也可以預留一個空的位置,作為滿的狀態,這個原理大家可以去網上查找環形隊列的相關知識,這個置空位恰好將head和tail隔開。

  • 但是現在有了信號量這個計數器,所以也能輕松地實現 線程間利用環形隊列同步的過程。?

整體代碼設計如下:

? ? ? ? 首先用類設計一個環形隊列RingQueue,并封裝一些接口push()和pop(),成員變量為一個vector數組(用數組模擬實現環形隊列)、int num_ 用來表示環形隊列的大小,c_step表示消費者的下標,p_step表示生產者的下標,然后有兩個信號量space_sem_data_sem_,分別表示空間資源信號量和數據資源信號量。一開始的時候沒有數據,所有空間都可使用,所以我們將空間資源信號量space_sem_設置為環形隊列的長度ndata_sem_設置為0,表示沒有數據。

? ? ? ? 信號量的數據類型為sem_t,但是為了方便,我對信號量進行了一個封裝,類Sem,里面包含了信號量的初始化,p()和v()操作等,然后上面兩個信號量的類型就直接使用Sem.

然后在RingQueue類中,兩個接口push和pop的實現邏輯如下:

  • push():這是生產者所使用的,生產者關注的是空間資源,如果有空間就生產,沒有就停止。?生產后空間資源信號量space_sem_-1,但是數據資源信號量data_sem_+1,然后每次就在對應位置上寫入相應的數據即可,記得模上數組的長度,因為邏輯結構是一個環形隊列。
  • pop():這是消費者所使用的,消費者關注的是數據資源,有數據就消費,沒數據就不能消費。消費后數據資源信號量data_sem_-1,但是空間資源信號量space_sem+1,同樣地將對應位置的數據取出即可。

所以環形隊列RingQueue類和Sem類的代碼如下:

Ringqueue.hpp類

#pragma once
#include<iostream>
#include<pthread.h>
#include<vector>
#include "Sem.hpp"
using namespace std;const int g_default_num = 5;template<class T>
class RingQueue
{
public://對環形隊列進行初始化RingQueue(int default_num = g_default_num):ring_queue_(g_default_num),num_(g_default_num),c_step(0),p_step(0),space_sem_(default_num),data_sem_(0){}~RingQueue(){}//生產者:關注空間資源void push(const T& in){space_sem_.p();ring_queue_[p_step++] = in;p_step %= num_;data_sem_.v();}//消費者:關注數據資源void pop(T* out){data_sem_.p();*out = ring_queue_[c_step++];c_step %= num_;space_sem_.v();}private:vector<T> ring_queue_;int num_;int c_step;//消費者下標int p_step;//生產者下標Sem space_sem_;Sem data_sem_;
};

Sem.hpp類

#pragma once
#include "ringQueue.hpp"
#include <semaphore.h>class Sem
{
public:Sem(int val){sem_init(&sem_,0,val);}void p(){sem_wait(&sem_);}void v(){sem_post(&sem_);}~Sem(){sem_destroy(&sem_);}
private:sem_t sem_;
};

然后我們對代碼進行測試,測試代碼如下,和上一節的測試代碼幾乎一樣:

#include "ringQueue.hpp"
#include<sys/types.h>
#include<unistd.h>
#include <time.h>
using namespace std;
void* consumer(void* args)
{RingQueue<int>* rq = (RingQueue<int>*)args;while(true){int x;//1.從環形隊列中拿取數據rq->pop(&x);//2.進行一定的處理cout << "消費: " << x << endl; }
}
void* producter(void* args)
{RingQueue<int>* rq = (RingQueue<int>*)args;while ((true)){//1.構建數據或任務對象 -- 一般可以從外部來,不要忽略時間消耗問題int x = rand() % 100 + 1;cout << "生產: " << x << endl; //2.推送到環形隊列中rq->push(x);//完成生產的過程// sleep(1);}}int main()
{srand((unsigned int)time(nullptr) ^ getpid() ^ 12366 );RingQueue<int>* rq = new RingQueue<int>();pthread_t c,p;// rq->debug();pthread_create(&c,nullptr,consumer,(void*)rq);pthread_create(&p,nullptr,producter,(void*)rq);pthread_join(c,nullptr);pthread_join(p,nullptr);return 0;
}

代碼也成功的執行了:

?


? ? ? ? 上面是單線程,即單生產者,單消費者。

????????我們也改成多線程并發執行的,這也是生產消費者模型的意義所在。

????????當多線程并發執行時,如果兩個線程 同時訪問臨界資源可能出錯,所以需要在臨界資源前后加上鎖,使得只能有一個線程可以訪問臨界資源。

? ? ? ? 但是這樣和單線程有什么區別啊,都是單線程訪問,多線程的意義在哪里?

? ? ? ? 首先不要狹隘的認為,把任務或數據放在交易場所就是生產和消費了。將數據或任務拿到之后的處理,才是最耗費時間的,雖然拿的時候是加鎖一個個拿的,但是處理的時候,卻是一起處理的所以生產消費者模型主要意義體現在可以并發的處理任務

  • 生產的本質:私有的任務 -> 公共空間中
  • 消費的本質:公共空間中 -> 私有的

信號量本質是一把計數器 那計數器的意義是什么?

????????可以不用進入臨界區,就可以得知資源的情況,甚至可以減少臨界區內部的判斷。

申請鎖 -> 判斷臨界資源和訪問 -> 釋放鎖 ---> 本質我們并不清楚臨界資源的情況,信號量要提前預設資源的情況,而且在pv變化中,我們在外部就可以知道臨界資源的情況.

? ? ? ? 所以我們在RingQueue類中,加入兩個鎖,分別為生產者和消費者:

?主函數中,創建多個線程:

?

?運行后,可以發現不同的線程生產和消費任務了。

這便是本章的全部內容了,主要講述了POSIX信號量,即基于環形隊列的生產消費者模型的一個實現。

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

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

相關文章

記K8S集群工作節點,AnolisOS 8.6部署顯卡驅動集成Containerd運行時

1、安裝gcc #安裝編譯環境 yum -y install make gcc gcc-c2、下載顯卡驅動 點擊 直達連接 nvidia高級搜索下載歷史版本驅動程序&#xff08;下載歷史版本驅動&#xff09; https://www.nvidia.cn/Download/Find.aspx?langcn3、安裝驅動 安裝顯卡驅動 ./NVIDIA-Linux-x86…

windows結束explorer進程后桌面白屏解決

背景 結束進程時一不小心一起刪掉explorer.exe &#xff0c;這個文件結束桌面就一片白 &#xff0c; 解決&#xff1a; 不要關機&#xff0c;同時按鍵盤上ctrlshiftesc ,重新進入任務管理器&#xff0c;接著點“進程”選項&#xff0c;按左上角文件選項&#xff0c;進入下拉菜單…

備份或同步數據?跨國大文件傳輸的不同需求與解決方案

信息化時代的到來&#xff0c;使得許多個人、組織、企業在日常生活中都需要對數據進行備份或同步。但不同的應用場景和需求&#xff0c;也需要不同的備份和同步方式。而在跨國大文件傳輸方面&#xff0c;更是需要根據不同需求選擇合適的傳輸方案。下面將分別介紹備份與同步數據…

BeanFactoryApplicationContext之間的關系

1**.BeanFactory與ApplicationContext之間的關系** &#xff08;1&#xff09;從繼承關系上來看&#xff1a; ? BeanFactory它是ApplicationContext 的父接口 &#xff08;2&#xff09;從功能上來看&#xff1a; ? BeanFactory才是spring中的核心容器&#xff0c;而Appli…

設備管理是什么意思?

設備管理 使組織能夠管理和維護設備&#xff0c;包括虛擬機、物理計算機、移動設備和 IoT 設備。 設備管理是任何組織安全策略的關鍵組成部分。 它有助于確保設備安全、最新且符合組織策略&#xff0c;目的是保護公司網絡和數據免受未經授權的訪問。 由于組織支持遠程和混合員…

MySQL 奇遇記三則

公司新項目&#xff0c;要使用 MySQL 數據庫。 第一次使用 MySQL&#xff0c;有點小激動。聽說過 N 多次&#xff0c;這一次終于用上了。 為什么是奇遇記&#xff1f; 因為在網上幾乎搜索不到別人遇到和我一樣的問題。 系統 &#xff1a;WINDOWS10X64 中文版 數據庫&#xf…

【數學建模】-- 數學規劃模型

概述&#xff1a; 什么是數學規劃&#xff1f; 數學建模中的數學規劃是指利用數學方法和技巧對問題進行數學建模&#xff0c;并通過數學規劃模型求解最優解的過程。數學規劃是一種數學優化方法&#xff0c;旨在找到使目標函數達到最大值或最小值的變量取值&#xff0c;同時滿足…

VGG簡單學習

VGG簡單學習 簡單介紹 在AlexNet網絡的基礎上&#xff0c;為了設計深層神經網絡&#xff0c;牛津大學設計了VGG網絡,采用塊的設計理念&#xff0c;將AlexNet中多個重復的卷積層和池化層組成一個塊 論文中&#xff0c;使用3x3卷積核&#xff0c;padding1的卷積層 和帶有2x2的匯…

1046:判斷一個數能否同時被3和5整除

【題目描述】 判斷一個數n 能否同時被3和5整除&#xff0c;如果能同時被3和5整除輸出YES&#xff0c;否則輸出NO。 【輸入】 輸入一行&#xff0c;包含一個整數n。&#xff08; -1,000,000 < n < 1,000,000&#xff09; 【輸出】 輸出一行&#xff0c;如果能同時被3…

WebMagic - 創意前端項目集合(點擊鏈接可在電腦上查看效果)

WebMagic - 創意前端項目集合 歡迎來到 WebMagic 倉庫&#xff01;這里匯集了一系列令人驚嘆的前端項目&#xff0c;涵蓋了HTML5、CSS3和JS等多項技術。無論你是前端開發者、設計師&#xff0c;還是對創意互動內容感興趣的人&#xff0c;這個倉庫都將為你帶來無盡的驚喜。 每…

Java Vue 前后端 關于時間格式數據的處理方法

前端使用 elment-ui 組件 el-date-picker 其中組件需要格式化時間&#xff0c;增加屬性 value-format"yyyy-MM-dd" 后端 Java 接收參數類型 后端Dto 使用Date接收&#xff0c;并添加JsonFormat注解 JsonFormat(pattern"yyyy-MM-dd") private Date testTi…

Python比較兩個Cookie之間的差異

python如何比較兩個cookie字符串之間的差異 pip安裝&#xff1a; pip install datedays 代碼&#xff1a; cookie1 JSESSIONID123456789; key1jiuliang; key22023; key31538; cookie2 JSESSIONID123456789; key1jiuliang; key40818; print(datedays.cookie_difference(…

LangChain手記 Chains

整理并翻譯自DeepLearning.AILangChain的官方課程&#xff1a;Chains&#xff08;源代碼可見&#xff09; Chains 直譯鏈&#xff0c;表達的意思更像是對話鏈&#xff0c;對話鏈的背后是思維鏈 LLM Chain&#xff08;LLM鏈&#xff09; 首先介紹了一個最簡單的例子&#xff0c…

代碼審計-java項目-組件漏洞審計

代碼審計必備知識點&#xff1a; 1、代碼審計開始前準備&#xff1a; 環境搭建使用&#xff0c;工具插件安裝使用&#xff0c;掌握各種漏洞原理及利用,代碼開發類知識點。 2、代碼審計前信息收集&#xff1a; 審計目標的程序名&#xff0c;版本&#xff0c;當前環境(系統,中間件…

圖數據庫_Neo4j和SpringBoot整合使用_實戰創建明星關系圖譜---Neo4j圖數據庫工作筆記0010

然后我們再來看一下這個明星關系圖譜 可以看到這里 這個是原來的startRelation 我們可以寫CQL去查詢對應的關系 可以看到,首先查詢出來以后,然后就可以去創建 我們可以把寫的創建明星關系的CQL,拿到 springboot中去執行 可以看到,這里我們先寫一個StarRelationRepository,然…

Java二分法查找

二分法&#xff1a;首先需要一個由小到大排序好的數組&#xff0c;先找到其中間值&#xff0c;然后進行比較如果比較中間值大的話則向前找。如果比要找的小&#xff0c;則向后找。 代碼實現&#xff1a; //定義查詢方法 public static int searchTarget(int[] nums, int targ…

wireshark界面內容含義

網絡分析工具——WireShark的使用&#xff08;超詳細&#xff09;_世間繁華夢一出的博客-CSDN博客 wireshark抓包數據&#xff1a;理解與分析_wireshark里面length_ 佚名的博客-CSDN博客

【圖書推薦 | 測試】—《測試設計思想》

前言 隨著科技的不斷發展&#xff0c;互聯網的不斷進步&#xff0c;日益出現了一種趨勢&#xff1a;測試設計將成為一種跨領域的綜合性工作&#xff0c;測試者將成為一種跨領域的通用型人才。由此清華大學出版社推出了一本名為《測試設計思想》的書籍&#xff0c;由知名專家周…

PHP-FPM進程排查

1、查看php-fpm的進程個數 ps -ef |grep "php-fpm"|grep "pool"|wc -l2、查看每個php-fpm占用的內存大小 ps -ylC php-fpm --sort:rss3.查看PHP-FPM在你的機器上的平均內存占用 ps --no-headers -o "rss,cmd" -C php-fpm | awk { sum$1 } END…

防止 JavaScript 中的正則表達式回溯

防止 JavaScript 中的正則表達式回溯 正則表達式是用于在軟件應用程序中操作和驗證文本的強大工具。然而&#xff0c;某些正則表達式模式可能容易受到回溯的影響&#xff0c;這可能會導致超線性運行時&#xff0c;并可能導致DoS攻擊。在本文中&#xff0c;我們將探討什么是回溯…