【Linux庖丁解牛】— 多線程同步 !

1. 什么是線程同步

為什么會有線程同步,那一定是有了新問題。互斥可以解決臨界資源被同時訪問的問題,但是純互斥也會帶來新的問題。由于當前被執行的線程離cpu最近【其他線程被阻塞掛起還要被喚醒】,所以,當前進程對于競爭鎖天然就有極大的優勢。這勢必會導致當前線程重復申請和釋放鎖,其他線程很難拿到鎖,也就會造成線程饑餓的問題。純粹的互斥,不高效,也不公平!

因此,我們提出新的要求,當前線程一旦釋放鎖就不能立即申請鎖,外面的線程也不能亂作一團的競爭鎖,必須排隊申請鎖。而剛釋放鎖的線程要想再次申請鎖,就必須排到隊列的末尾!!

我們把多個執行流在臨界區安全的前提下,按照順序依次訪問臨界資源叫做線程同步!

?2. 條件變量

> 理解條件變量

? 當?個線程互斥地訪問某個變量時,它可能發現在其它線程改變狀態之前,它什么也做不了。

? 例如?個線程訪問隊列時,發現隊列為空,它只能等待,直到其它線程將?個節點添加到隊列 中。這種情況就需要用到條件變量

為什么需要用到條件變量呢??因為線程a需要訪問隊列,線程b添加節點。這兩個線程互相競爭鎖,但是,只有隊列中添加節點后【即滿足一定的條件】,線程a拿到鎖才能做有效動作。因此,我們引入了條件變量。

當每個線程a訪問隊列時發現并不滿足條件,那么就掛起到隊列中等待。當線程b將節點添加之后,滿足了條件變量,此時再喚醒等待隊列中的一個或全部線程讓他們來訪問臨界資源。這樣做就高效多了!

> 條件變量接口

定義和初始化條件變量和互斥鎖的使用和互斥鎖一樣。

?這一批接口就是線程在指定條件變量下等待,很好理解。

喚醒在指定條件變量下等待的一個或所有線程。

> 使用條件變量接口demon?

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <string>const int num = 5; // 創建5個線程
int cnt = 0;// 定義初始化鎖和條件變量
pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t gcond = PTHREAD_COND_INITIALIZER;void *routine(void *args)
{std::string name = static_cast<char *>(args);while (true){// 加鎖pthread_mutex_lock(&glock);// 使用條件變量等待pthread_cond_wait(&gcond, &glock);std::cout << name << "計算" << cnt << std::endl;cnt++;// 解鎖pthread_mutex_unlock(&glock);}return nullptr;
}int main()
{std::vector<pthread_t> tids;// 創建5個線程for (int i = 0; i < num; i++){char *name = new char[64];snprintf(name, 64, "線程%d", i);pthread_t tid;pthread_create(&tid, nullptr, routine, name);tids.push_back(tid);// 每個1秒創建一個線程sleep(1);}// 主線程每隔一秒主動喚醒線程while (true){std::cout << "喚醒線程" << std::endl;// pthread_cond_signal(&gcond);//每隔一秒主動喚醒一個線程pthread_cond_broadcast(&gcond); // 每隔一秒主動喚醒所有線程sleep(1);}for (auto &e : tids){pthread_join(e, nullptr);}return 0;
}

每次喚醒一個線程:?線程依次被喚醒在隊列中同步串行運行。

?每次喚醒所有線程:所有線程競爭鎖。

3. 生產者消費者模型?

在現實生活中,我們不是直接去工廠購物,而是在超市中購物,因為我們直接去工廠購物成本高,效率低。?

在生產者消費者模型中,一共有3種關系,2種角色,1個交易場所。【321原則】

三種關系:消費者和消費者之間【互斥競爭關系】,生產者和生產者之間【互斥競爭關系】,生產者和消費者之間【互斥和同步關系】。

兩種角色:生產者和消費者角色【由線程承擔】。

一個交易場所:以特定結構構成的一種”內存“空間。

為什么要有生產者消費者模型呢???

1. 生產過程和消費過程解耦。【由于互斥和同步機制,生產和消費之間是不互相干擾的】

2. 支持忙閑不均。【即便消費者線程不工作,生產者線程也可以不斷執行。反之也是如此】

3. 提高效率。【獲取任務和處理任務是并發的!!】

4. 基于阻塞隊列的生產者消費者模型

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

block_queue.hpp

#pragma once#include <iostream>
#include <pthread.h>
#include <queue>const int defaultcap = 5;template <typename T>
class block_queue
{bool is_empty(){return _q.size() <= 0;}bool is_full(){return _q.size() >= _cap;}public:block_queue(int num = defaultcap): _cap(num), _consum_sleep_num(0), _product_sleep_num(0){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_full_cond, nullptr);pthread_cond_init(&_empty_cond, nullptr);}// 消費T &consum(){pthread_mutex_lock(&_mutex);while (is_empty()){// 空了就在阻塞隊列等待被喚醒_consum_sleep_num++;pthread_cond_wait(&_empty_cond, &_mutex);_consum_sleep_num--; // 被喚醒}T &out = _q.front();_q.pop();// 隊列一定不滿,喚醒生產者if (_product_sleep_num != 0){std::cout << "喚醒生產者……" << std::endl;pthread_cond_signal(&_full_cond);}// 解鎖pthread_mutex_unlock(&_mutex);return out;}// 生產void product(const T &in){pthread_mutex_lock(&_mutex);while (is_full()){// 滿了就在阻塞隊列等待被喚醒_product_sleep_num++;pthread_cond_wait(&_full_cond, &_mutex);_product_sleep_num--; // 被喚醒}_q.push(in);// 隊列一定不空,喚醒消費者if (_consum_sleep_num != 0){std::cout << "喚醒消費者……" << std::endl;pthread_cond_signal(&_empty_cond);}// 解鎖pthread_mutex_unlock(&_mutex);}~block_queue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_full_cond);pthread_cond_destroy(&_empty_cond);}private:std::queue<T> _q;int _cap; // 隊列容量上限pthread_mutex_t _mutex;pthread_cond_t _full_cond;pthread_cond_t _empty_cond;int _consum_sleep_num;  // 消費者休眠個數int _product_sleep_num; // 生產者休眠個數
};

消費者這里為什么要寫成循環呢???假設生產者只生產了一個商品,但是生產者卻喚醒了一批消費者!!那么消費者就全部來競爭鎖,一個消費者消費完一個商品后釋放鎖。其他消費者并不在阻塞隊列中等待,而是在鎖上等待。如果消費者立即拿到鎖,生產者還沒有來得及生產,那么消費者就沒有商品可以消費了,但是此時消費者已近持有鎖區消費空隊列了,也就是_q.pop()。這樣在代碼層面就出問題了!!我們把這種情況稱為偽喚醒所以我們要在判斷上加上循環,即便被喚醒了,也要再次檢查隊列中商品是否為空

main.cc

#include "block_queue.hpp"
#include <unistd.h>// 生產者
void *product(void *args)
{int cnt = 1;block_queue<int> *bq = static_cast<block_queue<int> *>(args);while (true){sleep(1);bq->product(cnt);std::cout << "生產者生產了:" << cnt << std::endl;cnt++;}
}// 消費者
void *consum(void *args)
{block_queue<int> *bq = static_cast<block_queue<int> *>(args);while (true){//sleep(1);int t = bq->consum();std::cout << "生產者消費了:" << t << std::endl;}
}int main()
{// 申請阻塞隊列block_queue<int> *bq = new block_queue<int>();// 構建生產者和消費者pthread_t p, c;pthread_create(&p, nullptr, product, bq);pthread_create(&p, nullptr, consum, bq);pthread_join(p, nullptr);pthread_join(c, nullptr);return 0;
}

5. 封裝條件變量?

mutex.hpp

#pragma once
#include <iostream>
#include <pthread.h>
namespace mutex_module
{class mutex{public:mutex(){int n = pthread_mutex_init(&_lock, nullptr);(void)n;}void lock(){pthread_mutex_lock(&_lock);}void unlock(){pthread_mutex_unlock(&_lock);}~mutex(){int n = pthread_mutex_destroy(&_lock);}pthread_mutex_t* get(){return &_lock;}private:pthread_mutex_t _lock;};// 采?RAII?格,進?鎖管理class lock_guard{public:lock_guard(mutex &m) : _m(m){_m.lock();}~lock_guard(){_m.unlock();}private:mutex &_m;};
}

cond.hpp

#pragma once
#include <iostream>
#include <pthread.h>
#include "mutex.hpp"using namespace mutex_module;namespace cond_module
{class cond{public:cond(){pthread_cond_init(&_cond, nullptr);}void wait(mutex &lock){pthread_cond_wait(&_cond, lock.get());}void signal(){pthread_cond_signal(&_cond);}void broadcast(){pthread_cond_broadcast(&_cond);}~cond(){pthread_cond_destroy(&_cond);}private:pthread_cond_t _cond;};
}

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

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

相關文章

基于arduino uno r3主控的環境監測系統設計-1

準備設計arduino uno r3為主控的環境監測系統&#xff0c;通過傳感器采集TVOC&#xff08;總揮發性有機物&#xff09;、HCHO&#xff08;甲醛&#xff09;和eCO2&#xff08;等效二氧化碳&#xff09;數據&#xff0c;并顯示在LCD屏幕上&#xff0c;同時支持數據記錄到SD卡&am…

ITIL 4:云計算與微服務對組織架構的影響

這幾年&#xff0c;很多組織在推進數字化轉型時遇到一個共同的問題&#xff1a;業務節奏越來越快&#xff0c;但內部協作的“架構”卻越來越跟不上節奏。技術架構的變革&#xff0c;必須同步推動組織架構的重塑。特別是隨著云計算和微服務架構的廣泛應用&#xff0c;這種影響愈…

【Android】xml和Java兩種方式實現發送郵件頁面

三三要成為安卓糕手 一&#xff1a;xml中LinearLayout布局參數的使用 1&#xff1a;xml代碼 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http:/…

美林數據用大模型重構電能質量評估,讓隱蔽合規問題無所遁形

在“雙碳”目標驅動下&#xff0c;電網企業正加速推進數字化轉型&#xff0c;電能質量評估作為電力系統安全運行的核心環節&#xff0c;其合規性與效率直接影響著電網智能化水平。然而&#xff0c;傳統人工審核模式已難以應對海量報告與復雜標準——單份報告需20-30人天核對、關…

前端基礎 JS Vue3 Ajax

一、JSalert( .... ) //彈出框console.log( ....... ) //輸出到控制臺瀏覽器JS引入方式&#xff1a;1、內部腳本&#xff1a;將JS代碼定義在HTML頁面中位于<script></script>標簽之間2、外部腳本&#xff1a;將JS代碼寫在外部JS文件中&#xff0c;在HTML頁面中使用…

如何解決pip安裝報錯ModuleNotFoundError: No module named ‘notebook’問題

【Python系列Bug修復PyCharm控制臺pip install報錯】如何解決pip安裝報錯ModuleNotFoundError: No module named ‘notebook’問題 一、摘要 在使用 PyCharm 進行 Python 開發時&#xff0c;常常需要通過 pip install 安裝第三方包。但有時即便已經安裝成功&#xff0c;運行代…

一、Vue概述以及快速入門

什么是VueVue的快速入門代碼&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Vue快速入門</title><script src"js/vue.js"></script> </head> <bod…

模型的存儲、加載和部署

定義損失函數并以此訓練和評估模型 存儲模型可以只存儲state_dict或模型參數&#xff0c;每當需要部署經過訓練的模型時&#xff0c;創建模型的對象并從文件中加載參數&#xff0c;這是 Pytorch 創建者推薦的方法。 目錄 模型的存儲、加載 模型的部署 模型的存儲、加載 承接…

Java學習第七十部分——微服務架構

目錄 一、前言提要 二、核心優勢 三、核心技術棧 四、構建步驟 五、困難挑戰 六、總結歸納 一、前言提要 Java 微服務架構是一種使用 Java 技術棧構建分布式系統的方法論&#xff0c;它將單一的大型應用程序分解為一組小型、獨立、松耦合、可獨立部署和擴展的服務。每個服…

六邊形滾動機器人cad【7張】三維圖+設計書明說

摘 要 機械制造業是國家的重要產業,隨著時代的發展,智能化越來越在生活中變得普遍,工業的發展深深的影響著一個國家的經濟發展。全球經濟的發展帶領著機械工業在不斷的進步。隨著國外先進技術在我國的傳播,也影響著我國技術的發展,在全球經濟的大環境的推動下,大型四邊形…

人形機器人加快先進AI機器人開發

物理AI的新時代通用人形機器人專為快速適應現有的以人類為中心的城市和工業工作空間而構建&#xff0c;用以承擔枯燥、重復性或對體力要求高的工作任務。這些機器人正在從工廠車間走向醫療健康機構&#xff0c;通過自動化幫助人類工作&#xff0c;緩解勞動力短缺問題。但是&…

AI 驅動開發效能躍升:企業級智能開發全流程優化方案?

企業軟件開發正面臨 “三高困境”&#xff1a;需求變更頻率高、人力成本占比高、線上故障風險高。破解這些難題的核心在于構建 AI 驅動的全流程智能開發體系&#xff0c;通過系統化效能優化實現開發能力升級。? 需求分析作為開發起點&#xff0c;常因理解偏差導致后期返工。A…

時序數據庫 TDengine × Ontop:三步構建你的時序知識圖譜

在做設備預測性維護或能源管理分析時&#xff0c;你是否也曾思考過&#xff1a;如何才能讓機器“理解”我們收集的大量時序數據&#xff1f;工業現場的數據是結構化的&#xff0c;而語義分析、知識推理卻往往需要 RDF 等圖譜格式。換句話說&#xff0c;“會說話”的數據更聰明&…

Android啟動圖不拉伸且寬占滿屏幕

Android啟動圖不拉伸且寬占滿屏幕 一般啟動圖的做法&#xff1a; start_app_bg.xml <?xml version"1.0" encoding"utf-8"?> <layer-list xmlns:android"http://schemas.android.com/apk/res/android"><item><shape>&l…

rust-方法語法

方法語法 方法類似于函數&#xff1a;我們用 fn 關鍵字和一個名稱來聲明它們&#xff0c;它們可以有參數和返回值&#xff0c;并且包含一些在從其他地方調用該方法時運行的代碼。與函數不同&#xff0c;方法是在結構體&#xff08;或枚舉、trait 對象&#xff0c;分別在第6章和…

【C++】C++ 的入門語法知識1

本文主要講解C語言的入門知識&#xff0c;包括命名空間、C的輸入與輸出、缺省參數以及函數重載。 目錄 1 C的第一個程序 2 命名空間 1&#xff09; 命名空間存在的意義 2&#xff09; 命名空間的定義 3&#xff09; 命名空間的使用 3 C的輸出與輸入 1&#xff09; C中…

SpringBoot6-10(黑馬)

JWT令牌簡介&#xff1a;1.JWT全稱:JSON Web Token(https://iwt.io/)定義了一種簡潔的、自包含的格式&#xff0c;用于通信雙方以json數據格式安全的傳輸信息。2.組成: >第一部分:Header(頭)&#xff0c;記錄令牌類型、簽名算法等。例如:("alg":“HS256",“t…

智能制造場景195個術語的16個分類

說明&#xff1a;《智能制造典型場景參考指引&#xff08;2025年版&#xff09;》日前&#xff0c;由工信部辦公廳正式發布&#xff0c;將成為眾多制造型企業的工作綱領 1. 工廠數字化規劃設計&#xff08;1.1&#xff09;&#xff1a;在電腦上用專業軟件設計工廠布局、規劃生產…

[論文閱讀] 人工智能 + 軟件工程 | 微信閉源代碼庫中的RAG代碼補全:揭秘工業級場景下的檢索增強生成技術

微信閉源代碼庫中的RAG代碼補全&#xff1a;揭秘工業級場景下的檢索增強生成技術 論文標題&#xff1a;A Deep Dive into Retrieval-Augmented Generation for Code Completion: Experience on WeChatarXiv:2507.18515 A Deep Dive into Retrieval-Augmented Generation for Co…

RabbitMQ—仲裁隊列

上篇文章&#xff1a; RabbitMQ集群搭建https://blog.csdn.net/sniper_fandc/article/details/149312481?fromshareblogdetail&sharetypeblogdetail&sharerId149312481&sharereferPC&sharesourcesniper_fandc&sharefromfrom_link 目錄 1 Raft一致性算法…