線程(三) linux 同步

目錄

概念補充

條件變量

操作

例:多線程搶票

封裝

生產者消費者模型

生產者和消費者之間的關系

BlockQueue(阻塞隊列)

單生產單消費

信號量

簡介

操作

多生產者多消費者RingQueue(環形隊列)代碼

sem封裝? ?

信號量與鎖

小知識


概念補充

同步:在保證數據安全的前提下,讓線程能夠按照某種特定的順序訪問臨界資源,從而有效避免饑餓問題,叫做同步

競態條件:因為時序問題,而導致程序異常,我們稱之為競態條件.

條件變量

簡單來說,條件變量相當于一個阻塞隊列,將線程入隊列(等待),滿足條件就出隊列(喚醒)

操作

全局條件變量

和mutex相似,使用宏初始化一個全局的條件變量

局部條件變量

attr傳nullptr即可

等待

cond為條件變量,mutex為鎖

喚醒

signal為喚醒一個線程

broadcast為喚醒所有線程

例:多線程搶票

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <string>pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;int tickets = 100;
void* func(void* args)
{std::string name(static_cast<char*>(args));while(true){pthread_mutex_lock(&lock);pthread_cond_wait(&cond,&lock);if(tickets==0) {pthread_mutex_unlock(&lock);break;}std::cout<<name<<"  "<<--tickets<<std::endl;pthread_mutex_unlock(&lock);}return (void*)0;
}int main()
{pthread_t t1,t2,t3;pthread_create(&t1,nullptr,func,(void*)"t1");pthread_create(&t2,nullptr,func,(void*)"t2");pthread_create(&t3,nullptr,func,(void*)"t3");while(true){pthread_cond_signal(&cond);sleep(0.3);if(tickets==0){pthread_cond_broadcast(&cond);break;}}pthread_join(t1,nullptr);pthread_join(t2,nullptr);pthread_join(t3,nullptr);return 0;
}

每個線程執行到wait時會阻塞住,

為什么wait要傳鎖,

因為wait會讓線程等待,如果線程申請到了鎖等待,那么其他線程就無法申請到鎖,所以wait會先釋放鎖再等待,等被喚醒后先申請鎖再向下執行,保證互斥.

封裝

#pragma once
#include <iostream>
#include <pthread.h>
#include "mutex.hpp"class Cond
{pthread_cond_t _cond;
public:    Cond(){pthread_cond_init(&_cond,nullptr);}void Wait(LockGuard& lock){int n = pthread_cond_wait(&_cond,lock.Get());(void)n;}void Notifyone(){int n = pthread_cond_signal(&_cond);(void)n;}void Notifyall(){int n = pthread_cond_broadcast(&_cond);(void)n;}~Cond(){pthread_cond_destroy(&_cond);}
};

生產者消費者模型

和生活中的超市類似,就是讓不同線程作為生產產品者和消費產品者,臨界區作為廠家和消費者的中轉站超市.

為什么存在這樣的模型:

1.減少生產和消費過程中產生的成本

2.支持生產和消費的忙閑不均

3.維護松耦合關系


減少成本問題:? ?比如一個方便面廠家,生產方便面都是成批生產,而消費者一般只會按個按箱購買,如果兩者直接交易,廠家生產一袋方便面,消費者購買,這樣廠家的成本會很高;如果廠家生產一批,消費者也買不了一批產品,消費者成本會很高.

支持忙閑不均和松耦合關系問題:? ? 還是上面的例子,廠家可以生產一批放到超市,然后等消費者慢慢消費,生產和消費沒有直接關系

生產者和消費者之間的關系

生產者之間 : 互斥關系

消費者之間 : 互斥關系

生產者與消費者之間 : 互斥+同步關系

BlockQueue(阻塞隊列)

在多線程編程中阻塞隊列(Blocking Queue)是?種常?于實現?產者和消費者模型的數據結構。其與普通的隊列區別在于,當隊列為空時,從隊列獲取元素的操作將會被阻塞,直到隊列中被放入了元素;當隊列滿時,往隊列?存放元素的操作也會被阻塞,直到有元素被從隊列中取出(以上的操作都是基于不同的線程來說的,線程在對阻塞隊列進程操作時會被阻塞)

//<BlockQueue.hpp>
#pragma once
#include <iostream>
#include <pthread.h>
#include <queue>
#include "mutex.hpp"
#include "cond.hpp"//上面封裝的條件變量const static u_int32_t CAP = 5;template<typename T>
class BlockQueue
{std::queue<T> _q;u_int32_t _cap;pthread_mutex_t _lock;Cond _c_cond;//消費者Cond _p_cond;//生產者unsigned int _c_wait_num;//當前消費者等待的個數unsigned int _p_wait_num;//當前生產者等待的個數  為了防止喚醒次數過多或喚醒無意義bool isFull(){return _q.size()>=_cap;}bool isEmpty(){return _q.empty();}public: ? ?BlockQueue(u_int32_t cap = CAP) :_cap(cap){}void Enqueue(const T& data){{LockGuard lock(&_lock);while(isFull())   //while循環防止wait調用失敗和偽喚醒問題{_p_wait_num++;_p_cond.Wait(lock);_p_wait_num--;}_q.push(data);if(_c_wait_num>0)_c_cond.Notifyone();}}void Pop(T* retval){{LockGuard lock(&_lock);while(isEmpty())     //while循環防止wait調用失敗和偽喚醒問題{_c_wait_num++;_c_cond.Wait(lock);_c_wait_num--;}*retval = _q.front();_q.pop();if(_p_wait_num>0)_p_cond.Notifyone();}}~BlockQueue(){}};
單生產單消費
#include "blockqueue.hpp"
#include <iostream>
#include <string>
#include <unistd.h>struct Data
{BlockQueue<int>* bq;std::string name;
};void *consumer(void* args)
{Data* data = static_cast<Data*>(args);int retval = 0;while(true){data->bq->Pop(&retval);std::cout<<data->name<<"消費了   " <<retval<<std::endl;   //sleep(1);} return (void*)0;
}void *productor(void* args)
{Data* data = static_cast<Data*>(args);int num = 1;while(true){data->bq->Enqueue(num);std::cout<<data->name<<"生產了   "<<num++<<std::endl;sleep(1);}return (void*)0;
}int main()
{BlockQueue<int>* bq = new BlockQueue<int>();pthread_t c,p;Data ctd = {bq,"消費者"};pthread_create(&c,nullptr,consumer,(void*)&ctd);Data ptd = {bq,"生產者"};pthread_create(&p,nullptr,productor,(void*)&ptd);pthread_join(c,nullptr);pthread_join(p,nullptr);return 0;
}

對于多生產和多消費,BlockQueue.hpp也支持

信號量

簡介

信號量針對多線程并發訪問一塊資源中的不同部分.

本質上,就是一個描述資源數量的計數器

操作

sem_init? ?初始化信號量

pshared ? 用來表示線程使用還是進程使用,0表示線程間使用

value ?信號量的初始值

sem_destroy? 銷毀信號量

sem_wait? ?信號量--

sem_post? 信號量++

多生產者多消費者RingQueue(環形隊列)代碼

生產者關注空余空間資源,消費者關注數據資源

sem封裝? ?<sem.hpp>
#pragma once
#include <iostream>
#include <semaphore.h>#define NUM 5class Sem
{sem_t _sem;int _initnum;
public:Sem(int num = NUM):_initnum(num){sem_init(&_sem,0,_initnum);}void P(){int n = sem_wait(&_sem);(void)n;}void V(){int n = sem_post(&_sem);(void)n;}~Sem(){sem_destroy(&_sem);}
};
<mutex.hpp>
#pragma once
#include <iostream>
#include <unistd.h>class LockGuard
{pthread_mutex_t* _lock;public:LockGuard(pthread_mutex_t* lock):_lock(lock){pthread_mutex_lock(_lock);}    pthread_mutex_t* Get(){return _lock;}~LockGuard(){pthread_mutex_unlock(_lock);}
};
<cond.hpp>
#pragma once
#include <iostream>
#include <pthread.h>
#include "mutex.hpp"class Cond
{pthread_cond_t _cond;
public:    Cond(){pthread_cond_init(&_cond,nullptr);}void Wait(LockGuard& lock){int n = pthread_cond_wait(&_cond,lock.Get());(void)n;}void Notifyone(){int n = pthread_cond_signal(&_cond);(void)n;}void Notifyall(){int n = pthread_cond_broadcast(&_cond);(void)n;}~Cond(){pthread_cond_destroy(&_cond);}
};
<RingQueue.hpp>
#pragma once
#include <iostream>
#include "sem.hpp"
#include <vector>
#include "mutex.hpp"#define CAP 10template<typename T>
class RingQueue
{std::vector<T> _rq;int _cap;Sem _data_sem;Sem _space_sem;int _c_step;int _p_step;pthread_mutex_t _p_lock;pthread_mutex_t _c_lock;
public:RingQueue(int cap = CAP):_cap(cap) ,_rq(cap) ,_data_sem(0) ,_space_sem(cap),_c_step(0),_p_step(0){pthread_mutex_init(&_p_lock,nullptr);pthread_mutex_init(&_c_lock,nullptr);}void Enqueue(const T& data){   _space_sem.P();{LockGuard lock(&_p_lock);_rq[_c_step++] = data;_c_step%=_cap;}_data_sem.V();}void Pop(T* data){_data_sem.P();{LockGuard lock(&_c_lock);*data = _rq[_p_step++];_p_step%=_cap;}_space_sem.V();}~RingQueue(){pthread_mutex_destroy(&_p_lock);pthread_mutex_destroy(&_c_lock);}
};

<RingQueue.cpp>

#include <iostream>
#include "sem.hpp"
#include "mutex.hpp"
#include "cond.hpp"
#include <pthread.h>
#include "RingQueue.hpp"RingQueue<int> rq(10);void* consumer(void* args)
{while(true){int data = 0;rq.Pop(&data);std::cout<<"消費  "<<data<<std::endl;}return (void*)0;
}void* productor(void* args)
{int data = 1;while(true){rq.Enqueue(data);std::cout<<"生產  "<<data++<<std::endl;}return (void*)0;
}int main()
{pthread_t c[2],p[2];pthread_create(c,nullptr,consumer,nullptr);pthread_create(c+1,nullptr,consumer,nullptr);pthread_create(p,nullptr,productor,nullptr);pthread_create(p+1,nullptr,productor,nullptr);pthread_join(c[0],nullptr);pthread_join(c[1],nullptr);pthread_join(p[0],nullptr);pthread_join(p[1],nullptr);return 0;
}

信號量與鎖

為什么信號量不需要加鎖,信號量為空或為滿時,信號量完成了同步和互斥動作,信號量不為空和不為滿時,生產者和消費者訪問不同位置,也不需要加鎖


二元信號量(信號量初始值為1) :就是鎖

鎖:認為資源只有一份,申請鎖相當于信號量P操作(--),釋放鎖相當于信號量V操作(++),所以鎖是信號量的一種特殊情況

小知識

1.pthread開頭的函數一般返回值為0表示成功

2.生產和消費還可以傳遞函數,用來給線程派發任務

3.生產消費模型高效在哪里:生產者生產數據和消費者處理數據是并發的,兩者是松耦合關系,可以忙閑不均,這里效率高,而二者向阻塞隊列中進行放入和拿出屬于同步和互斥,效率不高

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

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

相關文章

Eclipse 生成 jar 包

Eclipse 生成 jar 包 引言 Eclipse 是一款功能強大的集成開發環境&#xff08;IDE&#xff09;&#xff0c;廣泛應用于 Java 開發領域。在 Java 開發過程中&#xff0c;將源代碼編譯成可執行的 jar 包是常見的需求。本文將詳細介紹在 Eclipse 中生成 jar 包的方法&#xff0c;包…

kafka--基礎知識點--0

kafka 架構 https://cloud.tencent.com/developer/article/2307892 19張圖 生產者架構 消息的磁盤存儲文件結構 https://cloud.tencent.com/developer/article/2307892 19張圖 produce消息分區策略 kafka–基礎知識點–5–生產者分區策略 ISR、OSR、AR 是什么&#xff1…

替換ngnix ssl 證書

1. 阿里云數字證書管理服務 -》SSL 證書管理 -》個人測試證書&#xff08;原免費證書&#xff09;-》查找相應域名的證書/新建證書&#xff0c;申請 -》下載證書&#xff0c;如果是ngnix服務器&#xff0c;就下載pem/key格式2.遠程連接服務器a.nginx -t :查看ngnix 配置文件在哪…

rabbitmq ACK

在消息隊列&#xff08;如 RabbitMQ&#xff09;中&#xff0c;**ACK&#xff08;Acknowledgement&#xff09;是消息確認機制**&#xff0c;用于確保消息被消費者成功處理。其核心作用是解決以下問題&#xff1a;mermaid復制代碼導出svg&#x1f4cc; ACK 的兩種模式1. 自動確…

性能遠超Spring Cloud Gateway!Apache ShenYu如何重新定義API網關!

Apache ShenYu Apache ShenYu是一個異步的&#xff0c;高性能的&#xff0c;跨語言的&#xff0c;響應式的 API 網關。 特點 ? 代理&#xff1a;支持Apache Dubbo&#xff0c;Spring Cloud&#xff0c;gRPC&#xff0c;Motan&#xff0c;SOFA&#xff0c;TARS&#xff0c;We…

質變科技亮相可信數據庫發展大會,參編《數據庫發展研究報告2025》

數據庫作為支撐數據存儲與計算的關鍵載體&#xff0c;在數據要素時代的重要性愈發凸顯。過去一年&#xff0c;全球數據庫新技術、新業態、新模式不斷涌現&#xff1b;我國數據庫應用創新于重點行業快速推進。隨著人工智能技術深刻變革&#xff0c;數據庫與AI融合趨勢愈發明顯。…

LVS(Linux Virtual Server)詳細筆記(理論篇)

一.詳解1. LVS概述LVS&#xff08;Linux Virtual Server&#xff09;是由章文嵩博士發起的開源負載均衡項目&#xff0c;通過在Linux內核中實現高性能四層交換能力&#xff0c;將多臺物理服務器組織成單一虛擬服務。它能夠處理百萬級并發連接&#xff0c;同時保持線性擴展能力&…

Oracle Data Pump 導入沖突解決

問題場景 使用 impdp 導入數據時遇到"對象已存在"錯誤&#xff0c;導致導入失敗。 核心解決方案 1. TABLE_EXISTS_ACTION 參數 impdp username/passworddatabase \ directoryDATA_PUMP_DIR \ dumpfileyour_dump_file.dmp \ TABLE_EXISTS_ACTIONREPLACE作用&#xff1…

汽車免拆診斷案例 | 2015款進口起亞索蘭托L車漏電

故障現象 一輛2015款進口起亞索蘭托L車&#xff0c;搭載D4HB發動機&#xff0c;累計行駛里程約為15萬km。車主反映&#xff0c;該車停放2天左右就因蓄電池虧電而無法起動&#xff0c;更換過蓄電池&#xff0c;但故障依舊&#xff0c;于是將車開至我廠檢修。故障診斷 接車后用…

mysql復制延遲如何處理

一、復制延遲的原因主庫增刪改并發大大表在做DDL從庫備份導致延遲大事務從庫機器配置差二、怎樣判斷延遲使用 SHOW SLAVE STATUS 命令Seconds_Behind_Master&#xff1a;表示從庫落后主庫的秒數&#xff08;若為 NULL&#xff0c;可能復制線程已停止&#xff09;對比位點字段名…

HertzBeat 監控 SpringBoot 使用案例

HertzBeat 監控 SpringBoot 使用案例 在云原生時代&#xff0c;Spring Boot應用的監控與可視化已然成為運維體系的核心環節&#xff0c;實時監控應用性能是保障系統穩定性的關鍵。 這篇文章將結合 HertzBeat 實現從指標采集、可視化到告警的一體化解決方案&#xff0c;并展示…

突破性量子芯片問世:電子與光子首次集成,開啟量子技術規模化應用新篇章

文丨浪味仙 排版丨浪味仙行業動向&#xff1a;3000字丨8分鐘閱讀內容提要在量子技術邁向規模化應用的征程中&#xff0c;研究人員迎來了重要突破。近日&#xff0c;波士頓大學、加州大學伯克利分校以及西北大學的科學家在《Nature Electronics》上發表論文&#xff0c;報告實現…

day30——零基礎學嵌入式之線程2.0

一、進程和線程的對比線程進程定義輕量級的進程。是進程中的執行單元&#xff0c;作為CPU調度的基本單位進程時程序的一次執行過程&#xff0c;作為CPU的資源分配的基本單位優勢創建以及切換速度塊-----效率高線程&#xff08;線程共享了進程的資源&#xff09;間共享資源方便創…

洛谷 P1395 會議

【題目鏈接】 洛谷 P1395 會議 【題目考點】 1. 樹形動規&#xff1a;樹的重心 本題為求樹的重心模板題 【解題思路】 樹的重心&#xff1a;相比于樹中其它結點&#xff0c;其所有的子樹中結點數最多的子樹的結點數最少&#xff0c;該結點就是這棵樹的重心。 另一種定義&…

Microsoft 365 Adoption Score功能深度解析:驅動企業數字化轉型的利器

在數字化轉型的浪潮中,Microsoft 365(原Office 365)憑借其強大的生產力工具和云服務生態,已成為全球企業和組織提升效率、協作和創新的核心平臺。然而,僅僅部署Microsoft 365并不足以充分發揮其潛力,關鍵在于如何推動員工高效采用這些工具,并將其融入日常工作流程。為此…

尺寸標注識別5 實例分割 roboflow | result.boxes獲取邊界框 | yolov8n-seg架構 torchinfo | 對直線關系不敏感

https://gitee.com/njsgcs/yolo-local 單標注一個尺寸線 100輪就百分百了 Sign in to Roboflow 有混起來的問題 roboflow訓練用的cocon-seg模型我網上找不到 上面這種比較麻煩 text的中心要在dt范圍內 屏幕點以下等同于按下save&#xff08;enter&#xff09; 取最長線段作…

敏捷開發卡在需求分析?飛算 JavaAI 加速需求確認與功能迭代

在敏捷開發中&#xff0c;需求分析常成為團隊推進的 “卡點”—— 模糊的需求描述、反復的需求變更、拆解落地難等問題&#xff0c;往往導致迭代周期延長。而飛算 JavaAI 作為專為 Java 開發設計的工具&#xff0c;正通過 “需求理解 - 接口設計 - 代碼生成” 的全流程智能化&a…

QT跨平臺應用程序開發框架(10)—— Qt窗口

目錄 一&#xff0c;關于窗口 二&#xff0c;菜單欄 2.1 菜單介紹 2.2 添加菜單 2.3 添加快捷鍵 2.4 添加其子菜單 2.5 添加分割線和圖標 三&#xff0c;工具欄 3.1 添加和使用工具欄 3.2 設置位置屬性 四&#xff0c;狀態欄 五&#xff0c;浮動窗口 六&#xff0c;對話框 6.1 …

git從本地倉庫添加到遠程倉庫

先創建&#xff0c;然后配置 Git 的全局用戶名和郵箱git config --global user.name "不吃糖o" git config --global user.email "1523944556qq.com" git config --global -l 查看設置的用戶名和郵箱如何生成SSH公鑰&#xff1f;ssh-keygen 生成sshkeyls ~…

鎖步核,為什么叫鎖步核?

“鎖步核”&#xff08;Lockstep Cores&#xff09;這一名稱源于其工作原理與軍事隊列行進中的“鎖步”&#xff08;Lockstep&#xff09;動作的類比。以下是詳細的說明整理&#xff1a;1. 軍事起源&#xff1a;什么是“鎖步”&#xff1f; 在傳統軍事訓練中&#xff0c;“鎖步…