《C++并發編程實戰》筆記(一、二)

一、簡介

抽象損失:對于實現某個功能時,可以使用高級工具,也可以直接使用底層工具。這兩種方式運行的開銷差異稱為抽象損失。

二、線程管控

2.1 線程的基本控制

1. 創建線程

線程相關的管理函數和類在頭文件:

#include <thread>

創建一個線程使用如下方法:

std::thread t(callable);
  • callable:線程函數,可以是任意的可調用對象
  • 線程對象創建后會立即啟動線程運行

2. 控制線程的結束

線程啟動后,必須顯式指定線程結束的方式:

阻塞等待其結束(匯合),使用如下方法:

t.join();
  • 只要調用了join(),主線程會阻塞等待該線程執行結束,join()執行結束后,隸屬于該線程的任何存儲空間都會被清除,且線程對象不再關聯到結束的線程
  • 成員函數t.joinable()返回線程對象t是否關聯到某個線程,當join()執行結束后t.joinable()會返回false
  • 只有關聯到某個線程的對象才能調用join()

讓線程后臺運行(分離),使用如下方法:

t.detach();
  • 線程對象必須關聯某個線程(joinable()true時)才能調用detach,調用后線程的歸屬權和控制權都轉移給C++運行時庫,它獨立于主線程繼續運行,直至線程函數運行結束,且C++運行時庫會保證線程退出后與之關聯的資源都被正確回收
  • detach的線程不再關聯實際的執行線程(joinable()會返回false),所以不可再調用join()

注意:如果線程對象銷毀時都沒有指定結束方式,則std::thread的析構函數會調用std::terminate()終止整個程序

3. 異常時保證線程正常結束

創建線程對象后,如果在指定線程結束方式前,因為執行其他代碼造成了異常,可能會導致指定線程結束方式的代碼被略過,從而導致程序終止。即:

void func() {try {// 線程 t 去執行 do_something() 函數std::thread t(do_something);// 此時如果發生異常等,下面的join可能無法被調用,退出func時程序會被中斷do_other_thing();} catch (...) {throw;}t.join();return;
}

解決方法:

  1. 一種解決方法是保證在程序的所有執行路徑都會指定線程結束方式
    void func() {try {std::thread t(do_something);do_other_thing();} catch (...) {// 保證一定會指定線程結束方式t.join();throw;}t.join();return;
    }
    
  2. 使用RAII方法,利用對象管理線程,在構造函數中創建線程,在析構函數中指定線程的結束方式
    class RaiiThreadGuard {public:explicit RaiiThreadGuard(std::thread &t) : t_(t) {} ~RaiiThreadGuard() {// 類對象離開作用域時一定會調用析構,保證一定會指定線程對象的結束方式t_.join();}// 將拷貝構造和拷貝賦值都定義為刪除的,避免編譯器優化造成重復調用析構RaiiThreadGuard(const RaiiThreadGuard &) = delete;RaiiThreadGuard& operator=(const RaiiThreadGuard &) = delete;private:std::thread& t_;
    };// main 函數中通過如下方式使用
    int main() {// 創建線程std::thread t(HelloFunction);// 使用 RAII 保證線程對象一定會調用 joinRaiiThreadGuard thread_guard(t);// 即使后續再執行其他代碼造成退出作用域,編譯器會保證執行 thread_guard 的析構指定線程的結束方式return 0;
    }

2.2 向線程函數傳遞參數

線程函數所需要的參數可以直接緊跟在std::thread的線程函數實參后:

  • 線程具有內部存儲空間,線程函數的實參會先使用拷貝構造函數,將std::thread的實參復制到創建的線程;在創建好的線程中,新復制出來的實參被當成臨時量,以右值形式傳遞給新線程中的線程函數
  • 根據參數的傳遞過程,如果線程函數包含非const引用形參,為避免在線程內執行時收到右值,需要通過std::refstd::thread傳遞實參(與std::bind函數的使用相同)
  • 類的非靜態成員函數第一個參數是指向對象的隱式this指針,如果想在線程中執行某個成員函數,需要將對象地址傳遞給成員函數的隱式this指針
class TestClass {
public:/** @brief  默認構造函數 */TestClass(){std::cout << "TestClass default constructor." << std::endl;std::cout << "std::thread::id: " << std::this_thread::get_id() << std::endl;}/** @brief  拷貝構造函數 */TestClass(const TestClass& ohter) {std::cout << "TestClass copy constructor." << std::endl;std::cout << "std::thread::id: " << std::this_thread::get_id() << std::endl;}/** @brief  移動構造函數 */TestClass(TestClass&& ohter) {std::cout << "TestClass move constructor." << std::endl;std::cout << "std::thread::id: " << std::this_thread::get_id() << std::endl;}/** @brief  類的內部函數 */void InnerFunction() {std::cout << "TestClass Inner Function." << std::endl;std::cout << "std::thread::id: " << std::this_thread::get_id() << std::endl;}
};void func(int num, std::string &str, TestClass obj) {str += std::to_string(num);
}// main 函數
std::string str("The num: ");
TestClass test_class;
// 參數直接作為 std::thread 的后續參數傳入
// 1. 對象會先調用拷貝構造復制到線程,再通過std::move()以右值復制給線程內的函數形參
// 2. 線程函數的引用形參要通過 std::ref() 傳遞
std::thread t(func, 3, std::ref(str), test_class);
t.join();
std::cout << str << std::endl;// 在線程中運行類的成員函數,需要將對象的地址傳遞給成員函數的隱式this指針
std::thread t2(&TestClass::InnerFunction, &test_class);
t2.join();/* 輸出
TestClass default constructor.
std::thread::id: 140737348195264
TestClass copy constructor.
std::thread::id: 140737348195264
TestClass move constructor.
std::thread::id: 140737348179520
The num: 3
TestClass Inner Function.
std::thread::id: 140737348179520
*/

2.3 轉移線程歸屬權

在某些情況下,如想要指定特定的函數等待線程結束,可能需要轉移線程的歸屬權。

std::threadstd::unique類似,由于獨占資源,其對象不能被復制,只支持移動

std::thread t1(some_function);
std::thread t2 = std::move(t1);

2.4 運行時確定線程數量

標準庫的靜態函數std::thread::hardware_concurrency()函數返回程序在執行時可以真正并發的線程數量:

  • 若信息無法獲取,返回0
  • 否則返回支持并發的線程數

2.5 標識不同線程

每個線程都有一個唯一的ID,該ID是一個std::thread::id類型的變量:

  • 可以使用線程對象的std::thread::get_id()函數返回線程對象的ID
  • 在程序中可以使用std::this_thread::get_id()函數獲取運行當前程序的線程ID
  • 默認構造創建的std::thread::id類型變量表示線程不存在

std::thread::joinable()函數會利用線程對象的ID確定返回值,即:

  • this.get_id() != std::thread::id() 則返回 true(判斷當前線程ID和默認構造的線程ID類型變量是否相同)
  • 否則返回false(表示線程對象沒有關聯到任何線程,線程不存在)

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

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

相關文章

數據結構——線性表(C語言實現)

寫在前面&#xff1a; 在前面C語言的結構體學習中&#xff0c;我提及了鏈表的操作&#xff0c; 學習數據結構我認為還是需要對C語言的數組、函數、指針、結構體有一定的了解&#xff0c;不然對于結構體的代碼可能很難理解&#xff0c;特別是一些書籍上面用的還是偽代碼&#xf…

OpenGL筆記一之基礎窗體搭建以及事件響應

OpenGL筆記一之基礎窗體搭建以及事件響應 總結自bilibili趙新政老師的教程 code review! 文章目錄 OpenGL筆記一之基礎窗體搭建以及事件響應1.運行2.目錄結構3.main.cpp4.CMakeList.txt 1.運行 2.目錄結構 01_GLFW_WINDOW/ ├── CMakeLists.txt ├── glad.c ├── main…

Linux基于centos7指令初學3

date指令 作用&#xff1a; date指令可以查看時間 這個指令可以進行格式化 格式&#xff1a;date %想要的內容 Y&#xff1a;年份 m&#xff1a;月份 d&#xff1a;日 H&#xff1a;時 M&#xff1a;分 S&#xff1a;秒 時間分界線可以由…

GIT相關操作,推送本地分支到遠程倉庫流程記錄學習

git流程 切換到源文件夾&#xff1a;cd 源文件夾克隆遠程倉庫&#xff1a;git clone [ssh]進入項目文件夾&#xff1a;cd .\project\查看本地分支&#xff1a;git branch獲取遠程倉庫更新&#xff0c;使遠程同步&#xff1a;git fetch查看所有分支&#xff08;包括遠程分支&am…

OJ-0712

示例1&#xff1a; input 8 123 124 125 121 119 122 126 123 output 1 2 6 5 5 6 0 0示例2&#xff1a; input 2 95 100 output 1 0示例3&#xff1a; input 2 100 95 output 0 1package com.wsdcode.od;import java.util.Scanner;public class Main {public static void m…

LabVIEW比例壓力控制閥自動測試系統

開發了一套基于LabVIEW編程和PLC控制的比例控制閥自動測試系統。該系統能夠實現共軌管穩定的超高壓供給&#xff0c;自動完成比例壓力控制閥的耐久測試、流量滯環測試及壓力-流量測試。該系統操作簡便&#xff0c;具有高精度和高可靠性&#xff0c;完全滿足企業對自動化測試的需…

安裝jenkins最新版本初始化配置及使用JDK1.8構建項目詳細講解

導讀 1.安裝1.1.相關網址1.2.準備環境1.3.下載安裝 2. 配置jenkins2.1.安裝插件2.2.配置全局工具2.3.系統配置 3. 使用3.1.配置job3.2.構建 提示&#xff1a;如果只想看如何使用jdk1.8構建項目&#xff0c;直接看3.1即可。 1.安裝 1.1.相關網址 Jenkins官網&#xff1a;https…

RabbitMq如何保證消息的可靠性和穩定性

RabbitMq如何保證消息的可靠性和穩定性 rabbitMq不會百分之百讓我們的消息安全被消費&#xff0c;但是rabbitMq提供了一些機制來保證我們的消息可以被安全的消費。 消息確認 消息者在成功處理消息后可以發送確認&#xff08;ACK&#xff09;給rabbitMq&#xff0c;通知消息已…

Hadoop-25 Sqoop遷移 增量數據導入 CDC 變化數據捕獲 差量同步數據 觸發器 快照 日志

章節內容 上節我們完成了如下的內容&#xff1a; Sqoop MySQL遷移到HiveSqoop Hive遷移數據到MySQL編寫腳本進行數據導入導出測試 背景介紹 這里是三臺公網云服務器&#xff0c;每臺 2C4G&#xff0c;搭建一個Hadoop的學習環境&#xff0c;供我學習。 之前已經在 VM 虛擬機…

計算機的錯誤計算(二十九)

摘要 &#xff08;1&#xff09;討論近似值的錯誤數字個數。有時&#xff0c;遇到數字9或0, 不太好確認近似值的錯誤數字個數。&#xff08;2&#xff09;并進一步解釋確認計算機的錯誤計算&#xff08;二十八&#xff09;中一個函數值的錯誤數字個數。 理論上&#xff0c;我…

py2neo常用語句

1.連接數據庫 Neo4j服務器默認的端口號就是7474,所以本地的主機就是"http://localhost:7474" 。 默認的用戶名密碼都是neo4j&#xff0c; # 連接數據庫&#xff0c;輸入個人配置 graph Graph("http://localhost:7474//browser/", auth("neo4j"…

百日筑基第十九天-一頭扎進消息隊列2

百日筑基第十九天-一頭扎進消息隊列2 消息隊列的通訊協議 目前業界的通信協議可以分為公有協議和私有協議兩種。公有協議指公開的受到認可的具有規 范的協議&#xff0c;比如 JMS、HTTP、STOMP 等。私有協議是指根據自身的功能和需求設計的協 議&#xff0c;一般不具備通用性&…

數學建模·熵權法

熵權法 一種計算評價指標之間權重的方法。熵權法是一種客觀的方法&#xff0c;沒有主觀性&#xff0c;比較可靠。 具體定義 熵權法的核心在于計算信息熵&#xff0c;信息熵反映了一個信息的紊亂程度&#xff0c;體現了信息的可靠性 具體步驟 Step1正向化處理 將所以評價指標轉…

智能家居裝修怎么布線?智能家居網絡與開關插座布置

打造全屋智能家居。計劃的智能家居方案以米家系列為主&#xff0c;智能家居聯網方案以無線為主。裝修前為了裝備智能家居做了很多準備工作&#xff0c;本文深圳僑杰智能分享一個智能家居裝修和布線方面的心得與實戰知識。希望能對大家的裝修有所幫助。 ?1.關于網絡 如果房子比…

HTML基本標簽(二)

HTML基本標簽&#xff08;二&#xff09; 表格標簽 table媒體元素audio 音頻vido 視頻 form 表單元素 表格標簽 table <!-- caption 代表表格標題相關屬性border 邊框cellpadding 設置單元格內填充cellspacing 設置單元格間空隙width 設置表格寬度&#xff0c;默認是內容撐…

Python-數據爬取(爬蟲)

~~~理性爬取~~~ 杜絕從入門到入獄 1.簡要描述一下Python爬蟲的工作原理&#xff0c;并介紹幾個常用的Python爬蟲庫。 Python爬蟲的工作原理 發送請求&#xff1a;爬蟲向目標網站發送HTTP請求&#xff0c;通常使用GET請求來獲取網頁內容。解析響應&#xff1a;接收并解析HTTP響…

結合實體類型信息1——基于本體的知識圖譜補全深度學習方法

1 引言 1.1 問題 目前KGC和KGE提案的兩個主要缺點是:(1)它們沒有利用本體信息;(二)對訓練時未見的事實和新鮮事物不能預測的。 1.2 解決方案 一種新的知識圖嵌入初始化方法。 1.3 結合的信息 知識庫中的實體向量表示&#xff0b;編碼后的本體信息——>增強 KGC 2基…

基于AT89C51單片機超聲波水位液位控制系統設計(含文檔、源碼與proteus仿真,以及系統詳細介紹)

本篇文章論述的是基于AT89C51單片機的1616點陣LED顯示器字符滾動顯示設計的詳情介紹&#xff0c;如果對您有幫助的話&#xff0c;還請關注一下哦&#xff0c;如果有資源方面的需要可以聯系我。 目錄 設計任務與要求 原理圖 仿真圖 代碼 系統論文 資源下載 設計任務與要求…

處理線程安全的列表CopyOnWriteArrayList 和Collections.synchronizedList

ConcurrentModificationException 是 Java 中的一種異常&#xff0c;用于指示在迭代集合時&#xff0c;該集合的結構發生了并發修改。 在 Java 中&#xff0c;許多集合類&#xff08;如 ArrayList, HashMap 等&#xff09;都不是線程安全的。如果一個線程在迭代集合的同時&…

IDEA的JAVA版本沒有8怎么辦

問題&#xff1a; 很多小伙伴會出現如下的情況&#xff0c;java的版本很高&#xff0c;沒有8 解決 更換IDEA內置的Server URL的鏡像地址 就是這個 把其中的地址換成 https://start.aliyun.com/ https://start.aliyun.com/ 我們可以看到JAVA 8就出現了