【linux】線程同步和生產消費者模型

線程同步

當我們多線程訪問同一個臨界資源時,會造成并發訪問一個臨界資源,使得臨界資源數據不安全,我們引入了鎖的概念,解決了臨界資源訪問不安全的情況,對于線程而言競爭鎖的能力有強有弱,對于之前就搶到鎖的線程,當他釋放鎖后,由于不用做什么準備工作,他競爭鎖的能力很強,導致這個線程反復的爭奪鎖,來訪問臨界資源,導致其他線程處于饑餓狀態
同步:同步問題是保證數據安全的情況下,讓我們的線程具有一定的順序性

解決方案

條件變量的引入
當多線程來訪問臨界資源時,首先不會讓他去訪問臨界資源,而是將這個線程放入條件變量維護的隊列中去,等待臨界資源就緒,舉個例子,一個幼兒園里面,到了飯點,而小朋友們是每一個線程,而飯就是臨界資源,每次只能有一個孩子在餐廳里面打飯,這就是鎖,每個孩子跑步速度不一樣,競爭鎖的能力不一樣,為了避免一個孩子一直在餐廳不走,一直吃飯,幼兒園老師做了這個規定,每個小朋友吃飯必須去排隊,剛吃完的孩子還想吃的話,必須排隊在隊的后面。而條件變量維護的隊列類比與排隊,每個線程訪問完臨界資源之后必須在條件變量維護的隊列后面排隊
在這里插入圖片描述
在這里插入圖片描述

條件變量接口介紹

1、主要應用函數:

pthread_cond_init()函數
功能:初始化一個條件變量
pthread_cond_wait()函數
功能:阻塞等待一個條件變量
pthread_cond_signal()函數
功能:喚醒至少一個阻塞在條件變量上的線程
pthread_cond_broadcast()函數
功能:喚醒全部阻塞在條件變量上的線程
pthread_cond_destroy()函數
功能:銷毀一個條件變量

函數分析

1.初始化一個條件變量

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); 

參2:attr表條件變量屬性,通常為默認值,傳NULL即可

也可以使用靜態初始化的方法,初始化條件變量:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

2.阻塞等待一個條件變量

 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 

函數作用:

阻塞等待條件變量cond(參1)滿足
釋放已掌握的互斥鎖(解鎖互斥量)相當于pthread_mutex_unlock(&mutex);
當被喚醒,pthread_cond_wait函數返回時,解除阻塞并重新申請獲取互斥鎖pthread_mutex_lock(&mutex);

3.喚醒至少一個阻塞在條件變量上的線程

int pthread_cond_signal(pthread_cond_t *cond);

4.喚醒全部阻塞在條件變量上的線程

int pthread_cond_broadcast(pthread_cond_t *cond);

5.銷毀一個條件變量

int pthread_cond_destroy(pthread_cond_t *cond);

代碼實現線程同步

1.makefile編寫

mycond:mycond.ccg++ -o mycond mycond.cc -std=c++11 -lpthread
.PHONY:clean
clean:rm -f mycond

2.mycond.cc

#include<iostream>
#include<unistd.h>
#include<pthread.h>
using namespace std;int cnt=0;
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;void*fun(void*args)
{pthread_detach(pthread_self());uint64_t num=(uint64_t)args;cout<<"pthread:"<<num<<"create success"<<endl;while(1){ pthread_mutex_lock(&mutex);cout<<"pthread:"<<num<<",cnt:"<<cnt++<<endl;pthread_mutex_unlock(&mutex);}}
int main()
{for( uint64_t i=0;i<5;i++)
{pthread_t tid;pthread_create(&tid,nullptr,fun,(void*)i);
}
while(1)
{}
}

代碼解釋:
初始化一個鎖,一個條件變量,臨界資源cnt,每個線程在自己要執行的fun函數內,需要訪問臨界資源cnt,對cnt++,在主函數中創建5個線程,為了防止主線程退出,所有線程都退出,所以讓主線程死循環,在每個fun函數里面實現線程分離,不需要主線程來等待回收其他線程,讓操作系統自己回收

在這里插入圖片描述
由于線程2競爭鎖的能力強,每次都是線程2來訪問臨界資源。
為了解決一個線程競爭鎖的能力強,使用線程同步,先實現代碼在來解釋

#include<iostream>
#include<unistd.h>
#include<pthread.h>
using namespace std;int cnt=0;
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;void*fun(void*args)
{pthread_detach(pthread_self());uint64_t num=(uint64_t)args;cout<<"pthread:"<<num<<"create success"<<endl;while(1){ pthread_mutex_lock(&mutex);pthread_cond_wait(&cond,&mutex);//新增行cout<<"pthread:"<<num<<",cnt:"<<cnt++<<endl;pthread_mutex_unlock(&mutex);}}
int main()
{for( uint64_t i=0;i<5;i++)
{pthread_t tid;pthread_create(&tid,nullptr,fun,(void*)i);
}
while(1)
{
sleep(1);//新增行
pthread_cond_signal(&cond);//新增行
cout<<"signal one thread..."<<endl;    //新增行
}
}

pthread_cond_wait函數可以將剛申請鎖的線程讓其加入隊列,讓其休眠,該函數還會讓對應的線程釋放鎖,這樣一輪下來,所有想訪問臨界資源的線程都會出現在條件變量維護的隊列中,等待喚醒,一次喚醒一個,讓他們去訪問臨界資源,在主線程中來喚醒在維護條件變量對應的隊列中其他線程,去訪問臨界資源,sleep作用防止打印太快看不到效果。
在這里插入圖片描述
五個線程按照順序去訪問臨界資源


cp問題(生產消費者模型)

在這里插入圖片描述
此時有三種關系:
生產者和生產者:互斥
消費者和生產者:互斥,同步(一個放,一個拿,肯定要保證順序問題)
消費者和消費者:互斥
3種關系:生產者和生產者,消費者和消費者,生產者和消費者
2種角色:生產者和消費者
1個交易場所:特定結構的內存空間

特點:支持忙閑不均(對比馮諾依曼結構)
生產和消費進行解耦

代碼實現生產消費者模型

makefile實現

cp:cp.ccg++ -o cp cp.cc -std=c++11 -lpthread
.PHONY:clean
clean:rm -f cp

cp.cc(未完成)


#include<iostream>
#include<pthread.h>
#include"blockqueue.hpp"
#include<unistd.h>using namespace std;
void*consumer(void*args)
{
Blockqueue<int>*cq=static_cast<Blockqueue<int>*>(args);
while(1)
{
//消費數據,將隊列中的數據pop
int data=cq->pop();
cout<<"消費了一個數據:"<<data<<endl;
}
}
void* productor(void*args)
{int data=0;
Blockqueue<int>*pq=static_cast<Blockqueue<int>*>(args);
while(1)
{ sleep(1);//生產數據放到隊列中去pq->push(data);cout<<"生產了一個數據:"<< data++ <<endl;
}}int main()
{
pthread_t p,c;
Blockqueue<int>*st=new Blockqueue<int>();pthread_create(&p,nullptr,productor,st);pthread_create(&c,nullptr,consumer,st);pthread_join(p,nullptr);
pthread_join(c,nullptr);//線程等待回收
delete st;
return 0;}

在這里插入圖片描述
主線程創建兩個線程,一個是生產者,一個是消費者,生產者將數據插入在阻塞隊列中,消費者將數據取出阻塞隊列中的數據,阻塞隊列相當于臨界資源,主線程中new一個阻塞隊列對象作為共享的資源,在各個線程要執行的函數里面,pthread_create最后一個參數可以是資源的起始地址,我們可以傳阻塞隊列類對象過去,讓生產者線程和消費者線程都可以訪問到
blockqueue.hpp

#pragma once
#include<iostream>
#include<queue>
#include<pthread.h>
using namespace std;template<class T>
class Blockqueue
{static const int defalutnum=5;
public:
Blockqueue(int maxcap=defalutnum)
:maxcap_(maxcap){pthread_mutex_init(&mutex_,nullptr);pthread_cond_init(&p_cond_,nullptr);pthread_cond_init(&c_cond_,nullptr);}
T pop()
{T out=q_.front();q_.pop();return out;
}
void push(const T&in)
{
q_.push(in);
}
~Blockqueue(){pthread_mutex_destroy(&mutex_);pthread_cond_destroy(& p_cond_);pthread_cond_destroy(& c_cond_);}
private:
queue<T>q_;
int maxcap_;
pthread_mutex_t mutex_;
pthread_cond_t p_cond_;
pthread_cond_t c_cond_;};

由于該阻塞隊列是兩者共享的,臨界資源,防止并發訪問,要實現安全保護,所以要使用鎖,因為生產者和消費者都會對臨界資源訪問,兩者對鎖的競爭能力不一樣,可能會導致饑餓問題,使用線程同步,所以要用條件變量
maxcap為隊列最大可以放幾個值,這是我們規定的,為了解決忙而不均,同步問題,達到最大,就讓生產者線程休眠,如果阻塞隊列的個數為0,就讓消費者線程休眠,所以這里需要兩個條件變量分別維護。


現在要解決的是臨界資源的保護,保證多線程并發安全,使用鎖,還要就是兩個線程競爭鎖的能力不同,可能導致另一個線程饑餓問題,使用線程同步
在這里插入圖片描述

blockqueue.hpp

#pragma once
#include<iostream>
#include<queue>
#include<pthread.h>
using namespace std;template<class T>
class Blockqueue
{static const int defalutnum=5;
public:
Blockqueue(int maxcap=defalutnum)
:maxcap_(maxcap){pthread_mutex_init(&mutex_,nullptr);pthread_cond_init(&p_cond_,nullptr);pthread_cond_init(&c_cond_,nullptr);max_water=(maxcap*2)/3;min_water=maxcap/3;}
T pop()
{ pthread_mutex_lock(&mutex_);while(q_.size()==0){pthread_cond_wait(&c_cond_,&mutex_);}T out=q_.front();q_.pop();if(q_.size()<min_water)pthread_cond_signal(&p_cond_);pthread_mutex_unlock(&mutex_);return out;
}
void push(const T&in)
{
pthread_mutex_lock(&mutex_);
while(q_.size()==maxcap_)
{pthread_cond_wait(&p_cond_,&mutex_);}
q_.push(in);
if(q_.size()>max_water)
pthread_cond_signal(&c_cond_);
pthread_mutex_unlock(&mutex_);
}
~Blockqueue(){pthread_mutex_destroy(&mutex_);pthread_cond_destroy(&p_cond_);pthread_cond_destroy(&c_cond_);}
private:
queue<T>q_;
int maxcap_;
pthread_mutex_t mutex_;
pthread_cond_t p_cond_;
pthread_cond_t c_cond_;
int max_water;
int min_water;
};

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

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

相關文章

系統架構設計師【第9章】: 軟件可靠性基礎知識 (核心總結)

文章目錄 9.1 軟件可靠性基本概念9.1.1 軟件可靠性定義9.1.2 軟件可靠性的定量描述9.1.3 可靠性目標9.1.4 可靠性測試的意義9.1.5 廣義的可靠性測試與狹義的可靠性測試 9.2 軟件可靠性建模9.2.1 影響軟件可靠性的因素9.2.2 軟件可靠性的建模方法9.2.3 軟件的可靠性模…

實物資產的市場主線將逐步回歸

民生證券認為&#xff0c;投資者逐漸意識到長期趨勢并沒有發生變化&#xff0c;這或許正是本周最大的變化。在預期博弈重回冷靜期后&#xff0c;去金融化背景下實物資源占優的市場主線也將逐步回歸。 1 高低切換后的冷靜期 從4月下旬至上周&#xff0c;A股市場呈現出由高位資產…

用windows server backup備份文件夾到網絡共享文件夾并恢復

一、備份 開始 運行windows server backup,在右邊的窗格中點擊“備份計劃” 選擇備份配置 因為我們要備份的是一個文件夾&#xff0c;所以&#xff0c;選“自定義”&#xff0c;卷即為磁盤分區。 選擇要備份的項 點擊添加項目&#xff0c;可依次添加多個備份項目。 勾選需要…

汽車MCU虛擬化--對中斷虛擬化的思考(2)

目錄 1.引入 2.TC4xx如何實現中斷虛擬化 3.小結 1.引入 其實不管內核怎么變&#xff0c;針對中斷虛擬化無非就是上面兩種&#xff0c;要么透傳給VM&#xff0c;要么由Hypervisor統一分發。汽車MCU虛擬化--對中斷虛擬化的思考(1)-CSDN博客 那么&#xff0c;作為車規MCU龍頭…

MySQL 視圖(2)

上一篇&#xff1a;MySQL視圖&#xff08;1&#xff09; 基于其他視圖 案例對 WITH [CASCADED | LOCAL] CHECK OPTION 進行釋義 創建視圖時&#xff0c;可以基于表 / 多個表&#xff0c;也可以使用 其他視圖表 / 其他視圖 其他視圖 的方式進行組合。 總結 更新視圖&#x…

【HTML】tabindex

當給 div 標簽以 button 角色&#xff1a; <div role"button">這時要指定其 tabindex&#xff0c;因此正確的寫法是&#xff1a; <div role"button" tabindex"0">索引值不應當大于0&#xff0c;見a11y-positive-tabindex

Open3D(C++) Ransac擬合多項式曲線

目錄 一、算法原理一、代碼實現三、結果展示本文由CSDN點云俠原創,Open3D(C++) Ransac擬合多項式曲線,爬蟲自重。如果你不是在點云俠的博客中看到該文章,那么此處便是不要臉的爬蟲與GPT生成的文章。 一、算法原理 RANSAC(Random Sample Consensus)是一種用于擬合模型的迭…

設計模式深度解析:分布式與中心化

設計模式在軟件開發中扮演著至關重要的角色,它們提供了一套經過驗證的解決方案,用于解決常見的設計問題。在分布式和中心化這兩種不同的系統架構中,設計模式的應用也有所不同。以下是對這兩種架構下設計模式的深度解析: 分布式系統設計模式 在分布式系統中,由于系統被拆…

004 仿muduo實現高性能服務器組件_Buffer模塊與Socket模塊的實現

?&#x1f308;個人主頁&#xff1a;Fan_558 &#x1f525; 系列專欄&#xff1a;仿muduo &#x1f339;關注我&#x1f4aa;&#x1f3fb;帶你學更多知識 文章目錄 前言Buffer模塊Socket模塊 小結 前言 這章將會向你介紹仿muduo高性能服務器組件的buffer模塊與socket模塊的實…

【Leetcode 706 】設計哈希映射——數組嵌套鏈表(限制哈希Key)

題目 不使用任何內建的哈希表庫設計一個哈希映射&#xff08;HashMap&#xff09;。 實現 MyHashMap 類&#xff1a; MyHashMap() 用空映射初始化對象void put(int key, int value) 向 HashMap 插入一個鍵值對 (key, value) 。如果 key 已經存在于映射中&#xff0c;則更新其…

MATLAB的plot3使用技巧|更改視角|例程分享鏈接

plot3命令 MATLAB的plot3函數是用來繪制3D圖形的函數。它可以將三維數據可視化為線段、點、曲線等形式。plot3函數可以用于繪制三維空間中的曲線、曲面、散點圖等。 plot3函數的基本用法是&#xff1a; plot3(X,Y,Z)&#xff1a;繪制三維線段&#xff0c;其中X、Y、Z分別是包…

兩個雙指針 的 “他“和“ 她“會相遇么? —— “雙指針“算法 (Java版)

本篇會加入個人的所謂魚式瘋言 ??????魚式瘋言:??????此瘋言非彼瘋言 而是理解過并總結出來通俗易懂的大白話, 小編會盡可能的在每個概念后插入魚式瘋言,幫助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能說的不是那么嚴謹.但小編初心是能讓更多人能接…

MySQL入門學習-查詢進階.UNION

UNION操作符用于合并兩個或多個SELECT語句的結果集。它可以將多個查詢結果合并為一個結果集&#xff0c;這在需要從多個表中獲取數據并將它們組合在一起時非常有用。下面是一個使用UNION的示例代碼&#xff1a; SELECT column1, column2,...FROM table1UNIONSELECT column1, c…

springboot kafka 提高拉取數量

文章目錄 背景問題復現解決問題原理分析fetch.min.bytesfetch.max.wait.ms源碼分析ReplicaManager#fetchMessages 背景 開發過程中&#xff0c;使用kafka批量消費&#xff0c;發現拉取數量一直為1&#xff0c;如何提高批量拉取數量&#xff0c;記錄下踩坑記錄。 問題復現 ka…

攻防對抗少丟分,愛加密幫您筑起第二防線

應用程序通常處理和存儲大量的敏感數據&#xff0c;如用戶個人信息、財務信息、商業數據、國家數據等&#xff0c;用戶量越大的應用程序&#xff0c;其需要存儲和保護的用戶數據越多。因此應用層長期是攻擊方的核心目標&#xff0c;傳統應用安全依靠防火墻(FireWall)、入侵檢測…

1.7 協議層次和服務模型

協議層次 網絡是一個復雜的系統! ? 網絡功能繁雜&#xff1a;數字信號的物理信 號承載、點到點、路由、rdt、進程區分、應用等 ?現實來看&#xff0c;網絡的許多構成元素和設備: ? 主機 ? 路由器 ? 各種媒體的鏈路 ? 應用 ? 協議 ? 硬件, 軟件 Q:如何組織和實現這個…

Linux上實現ssh免密通訊

Linux上實現ssh免密通訊 1.SSH互信原理2.SSH所需的RPM包3.兩臺機器實現互信4.常見問題及處理 1.SSH互信原理 SSH&#xff08;Secure Shell&#xff09;是一種安全的傳輸協議&#xff0c;它能讓Linux系統中的服務器和客戶端之間進行安全可靠的通訊。 SSH使用加密的傳輸方式&…

iOS組件化 方案 實現

iOS組件化 組件化的原因現在流行的組件化方案方案一、url-block &#xff08;基于 URL Router&#xff09;方案二、protocol調用方式解讀 方案三、target-action調用方式解讀 gitHub代碼鏈接參考 組件化的原因 模塊間解耦模塊重用提高團隊協作開發效率單元測試 當項目App處于…

網絡原理-四

一、續 當窗口大小為0,意味著緩沖區滿了,此時發送方,就因該暫停發送,發送方會周期性的除法 " 窗口探測包 " ,并不攜帶載荷,這樣的包對于業務不產生影響,只是為了觸發ACK,一旦查詢出來的結果是非0,緩沖區右有空間了,發送方就可以繼續發送. 二、擁塞控制 要限制發送方…

一步一步寫線程之十三隊列間的消息通知

一、線程和分布式的通信 隨著技術的不斷發展&#xff0c;多線程和分布式通信愈發的普及。那么在這種場景下的如何進行數據的通信&#xff0c;便成為了一個非常典型的問題。無論是多線程還是分布式&#xff0c;其實其抽象出來的通信機制都是類似的。或者說換句話&#xff0c;多…