【C++】多線程同步三劍客介紹

目錄

條件變量

頭文件

主要操作函數

1、等待操作

2、喚醒操作

使用示例

信號量

頭文件

主要操作函數

1、信號量初始化

2、等待操作(P操作)

3、信號操作(V操作)

4、獲取信號量值?

5、銷毀信號量

使用示例

互斥鎖

頭文件

使用示例


當我們需要給多個線程的指定執行順序的時候,我們通常有多種方法:

  • 條件變量
  • 信號量
  • 互斥鎖

在這篇文章里,會介紹如何使用這三種方式來為多個線程指定執行順序,以及在使用的時候需要主義的地方。

條件變量

????????條件變量是C++11引入的同步原語,用于在多線程環境中實現線程間的等待和通知機制。它允許一個或多個線程等待某個條件成立,當條件滿足時,其他線程可以通知等待的線程繼續執行,一般需要配合unique_lock使用。

頭文件

#include <condition_variable>

主要操作函數

1、等待操作

a)基本形式

void wait(std::unique_lock<std::mutex>& lock);

b)帶謂詞形式

template<class Predicate>
void wait(std::unique_lock<std::mutex>& lock, Predicate pred);

?兩者的區別在于處理虛假喚醒的情況比較明顯,這個在后面介紹哈。

2、喚醒操作

a)喚醒單個線程

void notify_one() noexcept;

特點:喚醒等待隊列中的一個線程,具體是哪個線程是未定義的

b)喚醒所有線程

void notify_all() noexcept;

特點:喚醒等待隊列中的所有線程,性能開銷比較大,但是確保所有等待線程都被喚醒。

這里需要介紹一下虛假喚醒的問題

????????虛假喚醒是指線程在沒有收到notify_one或者notify_all調用的情況,從wait狀態中被喚醒。為什么會出現虛假喚醒的情況呢?因為可能會出現系統信號中斷條件變量的等待(SIGINT),或者因為底層I/O操作等底層系統調用中斷,導致pthread_cond_wait() 被中斷返回,因此出現虛假喚醒的情況。

? ? ? ? 在上面,我們介紹了兩種等待的方式,他們在處理虛假喚醒的情況表現有所不同。

? ? ? ? 帶謂詞的等待方式,會自動處理虛假喚醒,不需要我們再進行手動處理,那么他是怎么做到自動處理的呢,他的內部實現等價如下代碼,就是在循環中不斷判斷條件是否滿足,以此來處理虛假喚醒的情況。

// 帶謂詞的wait()函數的內部實現等價于:
template<class Predicate>
void wait(std::unique_lock<std::mutex>& lock, Predicate pred) {while (!pred()) {        // 關鍵:自動循環檢查wait(lock);          // 調用基本的wait()}// 退出循環時,保證 pred() 返回 true
}

? ? ? ? 基本的等待方式?需要我們手動處理虛假喚醒的情況,如下代碼是有問題的:

void wrong_basic_wait() {std::unique_lock<std::mutex> lock(mtx);// 錯誤:只等待一次,不處理虛假喚醒cv.wait(lock);// 假設條件一定滿足 - 危險!if (data_ready) {process_data();}
}

? ? ? ? 如果因為底層系統調用中斷了等待,但是此時條件并不滿足,比如數據并未準備好,會出現未定義的情況,因此,我們需要模仿帶謂詞的等待方式的等價寫法,在循環中判斷,如下:

void correct_basic_wait() {std::unique_lock<std::mutex> lock(mtx);// 正確:使用循環處理虛假喚醒while (!condition_satisfied()) {cv.wait(lock);// 如果是虛假喚醒,循環會繼續等待// 如果條件真的滿足,循環會退出}// 這里保證條件一定滿足process_data();
}

使用示例

1114. 按序打印 - 力扣(LeetCode)https://leetcode.cn/problems/print-in-order/description/

class Foo {condition_variable m_cv;mutex m_mtx;int m_nFlg;
public:Foo() {m_nFlg=1;}void first(function<void()> printFirst) {// printFirst() outputs "first". Do not change or remove this line.unique_lock<mutex> lock(m_mtx);m_cv.wait(lock,[=](){return m_nFlg==1;});printFirst();m_nFlg=2;m_cv.notify_all();}void second(function<void()> printSecond) {// printSecond() outputs "second". Do not change or remove this line.unique_lock<mutex> lock(m_mtx);m_cv.wait(lock,[=](){return m_nFlg==2;});printSecond();m_nFlg=3;m_cv.notify_all();}void third(function<void()> printThird) {// printThird() outputs "third". Do not change or remove this line.unique_lock<mutex> lock(m_mtx);m_cv.wait(lock,[=](){return m_nFlg==3;});printThird();m_nFlg=1;m_cv.notify_all();}
};

信號量

信號量的本質就是一個非負整數計數器,支持兩個原子操作:P(等待/減少)、V(信號/增加)

頭文件

#include <semaphore.h>

主要操作函數

1、信號量初始化

int sem_init(sem_t *sem, int pshared, unsigned int value);

參數說明:

  • sem:指向信號量(sem_t)的指針
  • pshared:0表示線程間共享,非0表示進程間共享
  • value:信號量的初始值

返回值

  • 返回0:初始化成功
  • 返回-1:初始化失敗,同時設置errno的錯誤碼。

2、等待操作(P操作)

信號量等待有三種方式

a)sem_wait()-阻塞等待

int sem_wait(sem_t *sem);

特點:如果信號量值為0,線程會一直阻塞等待,知道信號量可用。

b)sem_trywait()-非阻塞等待

int sem_trywait(sem_t *sem);

特點:非阻塞等待,立即返回,不會等待,如果信號量不可用,立即返回-1,不會造成線程阻塞的情況,適用于輪詢場景

c)sem_timedwait() - 超時等待

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

特點:在指定時間內等待,超時后返回-1,使用絕對時間戳,不是相對時間。

這個參數比較多,這里演示下用法:

struct timespec結構體用于存儲超時時間:

  • tv_sec:秒數
  • tv_nsec:納秒數
#include <semaphore.h>
#include <iostream>
#include <time.h>
#include <errno.h>void timed_work() {struct timespec timeout;clock_gettime(CLOCK_REALTIME, &timeout);timeout.tv_sec += 5;  // 5秒后超時int result = sem_timedwait(&sem, &timeout);if (result == 0) {std::cout << "在超時前獲取到信號量" << std::endl;// 執行臨界區代碼sem_post(&sem);} else {if (errno == ETIMEDOUT) {std::cout << "等待超時,放棄獲取" << std::endl;}}
}

?下面的代碼作用是獲取當前的系統時間,CLOCK_REALTIME表示使用系統實時時鐘

clock_gettime(CLOCK_REALTIME, &timeout);

3、信號操作(V操作)

釋放信號量,也就是將信號量的值+1。

int sem_post(sem_t *sem);

4、獲取信號量值?

int sem_getvalue(sem_t *sem, int *sval);

5、銷毀信號量

這種只能用于未命名的信號量,比如我們直接定義的sem_t sem,就屬于未命名信號量

int sem_destroy(sem_t *sem);

使用示例

1114. 按序打印 - 力扣(LeetCode)https://leetcode.cn/problems/print-in-order/description/這題希望我們指定三個線程的執行順序,我們可以定義三個信號量來進行控制

class Foo {sem_t s1,s2,s3;
public:Foo() {sem_init(&s1,0,1);sem_init(&s2,0,0);sem_init(&s3,0,0);}~Foo() {sem_destroy(&s1);sem_destroy(&s2);sem_destroy(&s3);}void first(function<void()> printFirst) {// printFirst() outputs "first". Do not change or remove this line.sem_wait(&s1);printFirst();sem_post(&s2);}void second(function<void()> printSecond) {// printSecond() outputs "second". Do not change or remove this line.sem_wait(&s2);printSecond();sem_post(&s3);}void third(function<void()> printThird) {// printThird() outputs "third". Do not change or remove this line.sem_wait(&s3);printThird();sem_post(&s1);}
};

互斥鎖

頭文件

#include <mutex>

使用示例

因為互斥鎖比較簡單這里,直接展示使用示例:1114. 按序打印 - 力扣(LeetCode)https://leetcode.cn/problems/print-in-order/

class Foo {mutex mtx1,mtx2,mtx3;
public:Foo() {mtx2.lock();mtx3.lock();}void first(function<void()> printFirst) {// printFirst() outputs "first". Do not change or remove this line.mtx1.lock();printFirst();mtx2.unlock();}void second(function<void()> printSecond) {// printSecond() outputs "second". Do not change or remove this line.mtx2.lock();printSecond();mtx3.unlock();}void third(function<void()> printThird) {// printThird() outputs "third". Do not change or remove this line.mtx3.lock();printThird();mtx1.unlock();}
};

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

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

相關文章

《Java Web程序設計》實驗報告八 JSP+Servlet+JDBC+MySQL實現課程管理

目 錄 一、實驗目的 二、實驗環境 三、實驗步驟和內容 1、小組成員分工&#xff08;共計4人&#xff09; 2、實驗方案 3、實驗結果與分析 4、項目任務評價 四、遇到的問題和解決方法 五、實驗總結 一、實驗目的 1、掌握mysql的安裝、數據庫表單創建 2、掌握JDBC的鏈接…

基于數據挖掘的課程推薦系統研究

摘要本研究設計并開發了一套基于先進數據挖掘技術的智能化課程推薦系統。該系統創新性地采用了協同過濾算法與內容推薦算法相結合的混合推薦策略&#xff0c;通過深度分析學生在學習平臺上的歷史行為數據&#xff08;包括選課記錄、學習時長、測試成績等&#xff09;以及課程的…

【SCI 4區推薦】《Journal of Visual Communication and Image Representation》

期刊簡介&#xff1a;《視覺傳達與圖像表示雜志》&#xff08;Journal of Visual Communication and Image Representation&#xff09;致力于發表視覺傳達與圖像表示領域的最前沿研究&#xff0c;特別強調多學科交叉領域中的新技術和理論應用。這本期刊涵蓋的研究范圍廣泛&…

20250711_Sudo 靶機復盤

target:192.168.43.20 外部打點 &#xff08;文件上傳&#xff09; nmap掃一下&#xff0c;80,22 開放 掃目錄&#xff0c;發現 README.md [17:04:30] 200 - 664B - /Dockerfile [17:04:38] 200 - 34KB - /LICENSE …

STEP 7-Micro/WIN SMART 編程軟件:從入門到精通的使用指南

STEP 7-Micro/WIN SMART 編程軟件&#xff1a;從入門到精通的使用指南 在工業自動化控制領域&#xff0c;編程軟件是連接工程師與 PLC 的橋梁&#xff0c;而 STEP 7-Micro/WIN SMART 作為 S7-200 SMART PLC 的專用編程工具&#xff0c;以其友好的界面和高效的編程能力備受青睞。…

模型訓練與部署注意事項篇---resize

圖像大小的影響在 YOLOv 系列模型的訓練和推理部署過程中&#xff0c;圖像大小的選擇是影響模型性能&#xff08;精度、速度、泛化能力&#xff09;的關鍵因素之一。兩者的關系既相互關聯&#xff0c;又存在一定的靈活性&#xff0c;具體可從以下幾個方面詳細分析&#xff1a;一…

【Python】venv:配置獨立鏡像源

為某個特定的 venv 虛擬環境設置 pip 鏡像源&#xff0c;使得該環境下的 pip 安裝始終使用自定義鏡像源&#xff0c;不影響系統 pip&#xff0c;也不依賴用戶級配置文件。環境準備 1. 創建虛擬環境 python -m venv venv2. 激活虛擬環境Windows: .\venv\Scripts\activateLinux/m…

日本語言學校:簽證制度類 Prompt 的結構整理路徑與策略

日本語言學校&#xff1a;簽證制度類 Prompt 的結構整理路徑與策略 我們在構建語言留學語義系統的過程中&#xff0c;嘗試以“簽證風險”為例&#xff0c;探索如何讓結構信息被更好地保留下來。本文不介紹 Prompt 本身&#xff0c;也不夸大其作用&#xff0c;而是希望借此與更…

RFCOMM協議詳解:串口仿真與TCP/IP協議棧移植技術——面試高頻考點與真題解析

一、RFCOMM 協議核心考點與高頻面試問題1.1 協議基礎與核心功能考點解析&#xff1a;RFCOMM&#xff08;Radio Frequency Communication&#xff09;是藍牙協議棧中實現串口仿真的核心協議&#xff0c;基于 L2CAP 協議提供類似 RS-232 的可靠數據流傳輸。其核心功能包括&#x…

【編程實踐】利用open3d生成物體的最長邊方向并可視化

1 利用3d軟件生成一個長方體 邊長隨意&#xff0c;長度隨意 2 導出為模型文件并采樣為點云數據 從mesh表面進行采樣&#xff0c;點數根據自己需求進行設置&#xff0c;此處設置為100000。采樣結果&#xff1a;3 識別OBB外接框并可視化長邊方向import numpy as np import open3d…

1. 好的設計原則

目錄一、應該具備的性質二、面向對象設計原則三、詳解3.1 開閉原則3.2 單一職責原則3.3 里氏替換原則3.4 依賴倒置原則3.5 接口隔離原則3.6 合成復用原則3.7 迪米特原則一、應該具備的性質 可擴展性靈活性可插入性 二、面向對象設計原則 以下設計原則的重要性從高到低排列 …

深度學習圖像分類數據集—貓七種表情識別分類

該數據集為圖像分類數據集&#xff0c;適用于ResNet、VGG等卷積神經網絡&#xff0c;SENet、CBAM等注意力機制相關算法&#xff0c;Vision Transformer等Transformer相關算法。 數據集信息介紹&#xff1a;貓七種表情識別分類&#xff1a;[Angry, Disgusted, Happy, Normal, Sa…

002_Claude模型與定價

Claude模型與定價 目錄 Claude 4 模型系列模型功能對比定價策略計費說明企業定價使用建議 Claude 4 模型系列 Anthropic 推出了最新的 Claude 4 系列模型&#xff0c;提供不同性能等級以滿足各種需求&#xff1a; Claude Opus 4 定位&#xff1a;最強大、最智能的模型特點…

【牛客刷題】游游的字母串

文章目錄 一、題目介紹1.1 題目描述1.2 輸入描述:1.3 輸出描述:1.4 示例1二、解題二、解題思路2.1 核心問題2.2 關鍵策略三、算法分析3.1 為什么正確?3.2 復雜度分析四、模擬演練五、完整代碼一、題目介紹 題目:游游的字母串 1.1 題目描述 對于一個小寫字母而言,游游可以通…

docker容器高級管理-dockerfile創建鏡像

目錄一.構建LNMP架構1.構建nginx容器①拉取centos鏡像&#xff08;對鏡像做基礎架構&#xff09;②創建dockerfile工作目錄&#xff08;可以是一個服務的項目&#xff09;③創建dockerfile④創建啟動腳本2.構建mysql數據庫①創建mysql項目②配置dockersfile文件④創建鏡像文件3…

北京-4年功能測試2年空窗-報培訓班學測開-第四十九天

今天自習&#xff0c;在自習室嚶…今天效率不高&#xff0c;導致焦慮。不&#xff0c;或者該說&#xff0c;因為焦慮導致效率不高&#xff1f;沒有達到自己預期&#xff0c;對自己也不滿意臨近結課&#xff0c;突然有些迷茫&#xff0c;我知道我要做的還有很多&#xff0c;要學…

css選擇器的優先級以及用法

在 CSS 中&#xff0c;當多個選擇器同時作用于一個元素&#xff0c;并且為該元素的同一屬性設置了不同的值時&#xff0c;就需要依據選擇器的優先級來確定最終應用哪個樣式。本文詳細介紹 CSS 選擇器優先級的相關內容。 本文目錄一、單個選擇器的優先級1. 內聯樣式2. ID 選擇器…

hercules zos 安裝 jdk 8

首先到 IBM 官網下載相關 PAX 檔 (SDK8_31bit_SR8_FP45.PAX.Z) 和 SDK8_31bit_readme.txt https://www.ibm.com/support/pages/java-sdk-products-zos 在 hercules 裡加一些新卷用來存放這個 JDK (UAPP02)&#xff0c;UTMP02 也可以順便加上讓 OMVS IBMUSER 下多一些存放空間&…

張量索引操作

一.前言本期我們來說一下張量的索引操作&#xff0c;需要掌握張量不同索引操作&#xff0c;我們在操作張量時&#xff0c;經常需要去進?獲取或者修改操作&#xff0c;掌握張量的花式索引操作是必須的?項能?。二.簡單行、列索引import torchdata torch.randint(0, 10, [4, 5…

docker 啟動中間件

docker 啟動 MySQL # 創建目錄 mkdir -p /Users/dongdong/software/mysql/{conf,data}docker run -d \ -p 3306:3306 \ -v /Users/dongdong/software/mysql/conf:/etc/mysql/conf.d \ -v /Users/dongdong/software/mysql/data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORDroot \ -…