C++多線程學習(二):多線程通信和鎖

參考引用

  • C++11 14 17 20 多線程從原理到線程池實戰
  • 代碼運行環境:Visual Studio 2019

1. 多線程狀態

1.1 線程狀態說明

  • 初始化 (lnit):該線程正在被創建
  • 就緒 (Ready):該線程在就緒列表中,等待 CPU 調度
  • 運行 (Running):該線程正在運行
  • 阻塞 (Blocked):該線程被阻塞掛起,Blocked 狀態包括
    • pend (鎖、事件、信號量等阻塞)
    • suspend (主動 pend)
    • delay (延時阻塞)
    • pendtime (因為鎖、事件、信號量時間等超時)
  • 退出 (Exit):該線程運行結束,等待父線程回收其控制塊資源
    • 告訴操作系統把該線程相關資源釋放,不包含堆中的資源釋放

在這里插入圖片描述

1.2 競爭狀態和臨界區

  • 競爭狀態 (Race Condition)
    • 多線程同時讀寫共享數據
  • 臨界區 (Critical Section)
    • 讀寫共享數據的代碼片段(lock 和 unlock 之間的代碼)

避免競爭狀態策略,對臨界區進行保護,同時只能有一個線程進入臨界區

2. 互斥體和鎖

2.1 互斥鎖

  • thread_mutex.cpp
    #include <iostream>
    #include <thread>
    #include <string>
    #include <mutex>using namespace std;static mutex mux;void TestThread() {for (;;){	// 獲取鎖資源,如果沒有則阻塞等待(一次只能有一個線程拿到鎖)// 拿鎖的原則:盡晚申請、盡早釋放//mux.lock();           // 拿鎖方式一 if (!mux.try_lock()) {  // 拿鎖方式二:可以看到多個進程在競爭拿鎖的情況cout << "." << flush;this_thread::sleep_for(100ms);continue;}// 業務代碼cout << "=========" << endl;cout << "Test 001" << endl;cout << "Test 002" << endl;cout << "Test 003" << endl;cout << "=========" << endl;mux.unlock();  // 如果忘記釋放鎖,則會導致死鎖,所有線程都在等待this_thread::sleep_for(1000ms);}
    }int main(int argc, char* argv[]) {// 同時創建 10 個線程for (int i = 0; i < 10; i++) {thread th(TestThread);th.detach();}getchar();return 0;
    }
    

2.2 線程搶占不到資源

  • thread_mutex2.cpp
    #include <iostream>
    #include <thread>
    #include <string>
    #include <mutex>using namespace std;static mutex mux;void ThreadMainMux(int i) {for (;;){	mux.lock();cout << i << "[in]" << endl;this_thread::sleep_for(1000ms);mux.unlock();// 防止 unlock() 還未釋放完全就進入下一個 lock(),導致其他線程拿不到鎖this_thread::sleep_for(1ms);}
    }int main(int argc, char* argv[]) {// 同時創建 3 個線程for (int i = 0; i < 3; i++) {thread th(ThreadMainMux, i + 1);th.detach();}getchar();return 0;
    }
    

2.3 超時鎖 timed_mutex 應用

  • 可以記錄鎖的獲取情況,多次超時,可以記錄日志,獲取錯誤情況,避免長時間死鎖
  • timed_mutex.cpp
    #include <iostream>
    #include <thread>
    #include <string>
    #include <mutex>using namespace std;timed_mutex tmux;  // 支持超時的互斥鎖void ThreadMainTime(int i) {for (;;){	if (!tmux.try_lock_for(chrono::milliseconds(500))) {cout << i << "[try_lock_for timeout]" << endl;continue;}cout << i << "[in]" << endl;this_thread::sleep_for(2000ms);tmux.unlock();// 防止 unlock() 還未釋放完全就進入下一個 lock(),導致其他線程拿不到鎖this_thread::sleep_for(1ms);}
    }int main(int argc, char* argv[]) {getchar();// 同時創建 3 個線程for (int i = 0; i < 3; i++) {thread th(ThreadMainTime, i + 1);th.detach();}getchar();return 0;
    }
    

2.4 遞歸鎖 recursive_mutex(可重入)

  • 同一個線程中的同一把鎖可以鎖多次,避免一些不必要的死鎖
  • 組合業務:用到同一個鎖
  • recursive_mutex.cpp
    #include <iostream>
    #include <thread>
    #include <string>
    #include <mutex>using namespace std;recursive_mutex rmux;  // 支持可重入的互斥鎖void Task1() {rmux.lock();cout << "task1 [in]" << endl;rmux.unlock();
    }void Task2() {rmux.lock();cout << "task2 [in]" << endl;rmux.unlock();
    }void ThreadMainRec(int i) {for (;;){// 加鎖幾次對應的也要解鎖幾次rmux.lock();Task1();cout << i << "[in]" << endl;this_thread::sleep_for(2000ms);Task2();rmux.unlock();this_thread::sleep_for(1ms);}
    }int main(int argc, char* argv[]) {// 同時創建 3 個線程for (int i = 0; i < 3; i++) {thread th(ThreadMainRec, i + 1);th.detach();}getchar();return 0;
    }
    

2.5 共享鎖 shared_mutex(解決讀寫問題)

  • c++14 共享超時互斥鎖 shared_timed_mutex
  • 如果只有寫時需要互斥,讀取時不需要,用普通的鎖的話如何做
  • 按照如下代碼,讀取只能有一個線程進入,在很多業務場景中,沒有充分利用 CPU 資源

在這里插入圖片描述

  • shared_mutex.cpp
    #include <iostream>
    #include <thread>
    #include <string>
    #include <mutex>
    #include <shared_mutex>using namespace std;shared_timed_mutex stmux;  // 支持可重入的共享鎖 C++14// 讀取線程
    void ThreadRead(int i) {for (;;){stmux.lock_shared();cout << i << " Read" << endl;this_thread::sleep_for(500ms);stmux.unlock_shared();this_thread::sleep_for(1ms);}
    }// 寫入線程
    void ThreadWrite(int i) {for (;;){stmux.lock_shared();  // 只要沒有鎖定互斥鎖,共享鎖都是立即返回// 讀取數據stmux.unlock_shared();// 互斥鎖 寫入(同時只能一個線程寫入),共享鎖和互斥鎖都不能進入stmux.lock();  cout << i << " Write" << endl;this_thread::sleep_for(300ms);stmux.unlock();this_thread::sleep_for(1ms);}
    }int main(int argc, char* argv[]) {for (int i = 0; i < 3; i++) {thread th(ThreadWrite, i + 1);th.detach();}for (int i = 0; i < 3; i++) {thread th(ThreadRead, i + 1);th.detach();}getchar();return 0;
    }
    

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

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

相關文章

xss-labs靶場6-10關

文章目錄 前言一、靶場6-10關1、關卡62、關卡73、關卡84、關卡95、關卡10 總結 前言 此文章只用于學習和反思鞏固xss攻擊知識&#xff0c;禁止用于做非法攻擊。注意靶場是可以練習的平臺&#xff0c;不能隨意去尚未授權的網站做滲透測試&#xff01;&#xff01;&#xff01; …

在win10上安裝pytorch-gpu版本2

安裝anaconda即下載了python&#xff0c;還可以創建虛擬環境。 目錄 1.1 anaconda安裝 1.2 pytorch-gpu安裝 1.1 Anaconda安裝 anaconda的安裝請看我之前發的tensoflow-gpu安裝&#xff0c;里面有詳細的安裝過程&#xff0c;這里不做重復描述&#xff0c;傳送門 1.2 pyt…

羊大師提示,羊奶都有哪些驚人功效?

羊奶不僅是一種美味的健康飲品&#xff0c;在近年來備受矚目的的健康圈子里&#xff0c;羊奶還被賦予了更多的功效&#xff0c;成為一種備受推崇的保健品。羊奶不但富含營養&#xff0c;而且還有著非常多的益處&#xff0c;它能夠用來美容、保健&#xff0c;甚至還可以治療某些…

【Java】多線程-單例模式/volatile-指令重排序

單例模式即代碼中只有一個實例的模式 適用場景&#xff1a;有些場景下&#xff0c;有的類只能有一個對象&#xff0c;不能有多個 要注意&#xff1a;在單例模式下&#xff0c;要保證不能產生多個實例 1、餓漢模式 class Singleton{private static Singleton instance new …

Mybatis plus 簡介

簡介 MyBatis-Plus (opens new window)&#xff08;簡稱 MP&#xff09;是一個 MyBatis (opens new window)的增強工具&#xff0c;在 MyBatis 的基礎上只做增強不做改變&#xff0c;為簡化開發、提高效率而生。 官網:https://baomidou.com/pages/24112f/ 特性 無侵入&…

英語常見的21組重點必背短語

短語: at at once 立刻&#xff0c;馬上 at least 至少 at most 最多 at last 最后 at home 在家 at noon 在中午 at night 在夜晚 at times 有時&#xff0c;偶爾 at school 在上學 at table 在吃飯 at present 目前&#xff0c;現在 at work 在工作 at all 全然&#xff0c…

《QT從基礎到進階·三十八》QWidget實現炫酷log日志打印界面

QWidget實現了log日志的打印功能&#xff0c;不僅可以在界面顯示&#xff0c;還可以生成打印日志。先來看下效果&#xff0c;源碼放在文章末尾&#xff1a; LogPlugin插件類管理log所有功能&#xff0c;它可以獲取Log界面并能打印正常信息&#xff0c;警告信息和錯誤信息&…

runnergo全棧測試平臺

一、全棧測試平臺runnergo使用 官網 官方使用文檔 二、單接口測試 三、性能測試 1.性能測試 2.性能測試報告 四、自動化測試&#xff08;暫時不支持UI自動化&#xff0c;或許會上&#xff09;

Jmeter 壓測實戰保姆級入門教程

1、Jmeter本地安裝 1.1、下載安裝 軟件下載地址&#xff1a; https://mirrors.tuna.tsinghua.edu.cn/apache/jmeter/binaries/ 選擇一個壓縮包下載即可 然后解壓縮后進入bin目錄直接執行命令jmeter即可啟動 1.2 修改語言 默認是英文的&#xff0c;修改中文&#xff0c;點擊…

使用Java Servlet生成動態二維碼

文章目錄 引入ZXing庫創建QRCodeServlet部署到Servlet容器拓展功能1. 動態生成二維碼內容2. 調整二維碼尺寸3. 錯誤修正級別4. 日志輸出 結語 &#x1f389;歡迎來到Java學習路線專欄~探索Java中的靜態變量與實例變量 ☆* o(≧▽≦)o *☆嗨~我是IT陳寒&#x1f379;?博客主頁&…

【追求卓越04】數據結構--棧與隊列

引導 今天我們開始學習棧與隊列的內容&#xff0c;我覺得棧并不難&#xff0c;所以篇幅也就不會那么多了。在虛擬空間中&#xff0c;棧是用戶空間中的一種數據結構&#xff0c;它主要用于保存局部變量。那么問題來了&#xff0c;為什么用棧來保存局部變量&#xff0c;不用別的數…

Spring Boot 3 集成 Knife4j

基礎環境 SpringBoot : 3.0.6 Java: jdk-17.0.5 Maven: 3.6.1依賴 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xs…

Go 語言函數、參數和返回值詳解

函數是一組語句&#xff0c;可以在程序中重復使用。函數不會在頁面加載時自動執行。函數將通過調用函數來執行。 創建函數 要創建&#xff08;通常稱為聲明&#xff09;一個函數&#xff0c;請執行以下操作&#xff1a; 使用 func 關鍵字。指定函數的名稱&#xff0c;后跟括…

Java編程技巧:if-else優化實踐總結歸納

文/朱季謙 說實話&#xff0c;其實我很討厭在代碼里大量使用if-else&#xff0c;一是因為該類代碼執行方式屬于面向過程的&#xff0c;二嘛&#xff0c;則是會顯得代碼過于冗余。這篇筆記&#xff0c;主要記錄一些自己在工作實踐當中針對if-else的優化心得&#xff0c;將會不定…

10年開發工程師總結,8大主流程序員兼職平臺,月入30k不是夢!

今年互聯網行業陸續裁員減薪&#xff0c;許多人怨聲載道的同時也開始另謀出路。而對于程序員更是應該提早做好準備&#xff0c;活躍在兼職接單的最前沿。 我們程序員是一門技術工種&#xff0c;與互聯網其他行業相比薪水會相對高一點&#xff0c;不過錢也不是那么好賺的&#…

C++中類和動態內存分配

new關鍵字 在C中&#xff0c;內存分為棧和堆。棧中的對象生命周期較短&#xff0c;往往在作用域結束后就會銷毀&#xff0c;而堆中的對象生命周期較長&#xff0c;只有當使用delete或者程序結束時才會銷毀。而new則是將我們創建的對象分配到堆中&#xff0c;使對象可以跨作用域…

2023年【廣東省安全員B證第四批(項目負責人)】報名考試及廣東省安全員B證第四批(項目負責人)復審考試

題庫來源&#xff1a;安全生產模擬考試一點通公眾號小程序 廣東省安全員B證第四批&#xff08;項目負責人&#xff09;報名考試是安全生產模擬考試一點通總題庫中生成的一套廣東省安全員B證第四批&#xff08;項目負責人&#xff09;復審考試&#xff0c;安全生產模擬考試一點…

json_to_mask

修改后的json_to_dataset文件&#xff0c;直接復制替換你自己原始的json_to_dataset&#xff0c;建議保存一下原版import argparse import base64 import json import os import os.path as ospimport imgviz import PIL.Imagefrom labelme.logger import logger from labelme …

java:springboot單元測試spring-boot-starter-test

背景 Java的單元測試可以使用多個框架&#xff0c;其中比較流行的包括&#xff1a; JUnit&#xff1a;JUnit是Java單元測試最常用的框架&#xff0c;它提供了一套豐富的API&#xff0c;可以方便地編寫測試用例和測試套件。JUnit 5是JUnit的最新版本&#xff0c;引入了許多新功…

ElMessageBox中的子組件回調關閉函數

父組件中&#xff1a; const closeMessageBox () > {ElMessageBox.close();getList(); };const open () > {ElMessageBox({title: 添加商品,message: h(AddTaxExemption, { onClose: closeMessageBox }),customClass: custom-message-box, showConfirmButton: false,d…