C++11 thread

文章目錄

  • C++11 線程庫
  • 線程對象的構造方式
    • 無參的構造函數
    • 調用帶參的構造函數
    • 調用移動構造函數
        • thread常用成員函數
  • this_thread命名空間
  • join && detach
  • mutex

C++11 線程庫

線程對象的構造方式

無參的構造函數

1、調用無參的構造函數,調用無參的構造函數創建出來的線程對象沒有關聯任何線程函數,即沒有啟動任何線程

thread t1;

2、thread提供了移動賦值函數,當后續需要讓該線程對象與線程函數關聯時,可以以帶參的方式創建一個匿名對象,然后調用移動賦值將該匿名對象關聯線程的狀態轉移給該線程對象

void Print(size_t n , const std::string& s)
{for (size_t i = 0; i < n; i++){std::cout << std::this_thread::get_id() << ":" << i << std::endl;}
}
int main()
{int n = 10;// 創建n個線程執行Printstd::vector<std::thread> vthd(n);size_t j = 0;for (auto& thd : vthd){// 移動賦值thd = std::thread(Print, 10, "線程" + std::to_string(j++));}for (auto& thd : vthd){thd.join();}return 0;}

場景:

實現線程池的時候, 需要先創建一批線程,但,一開始這些線程什么也不做,當有任務到來時再讓這些線程來處理這些任務。

#include <iostream>
#include <vector>
#include <thread>
#include <functional>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <atomic>class ThreadPool {
public:ThreadPool(size_t threads = 4);  // 構造函數,可以指定線程池的大小~ThreadPool();  // 析構解函數void enqueue(std::function<void()> task);  // 添加任務到線程池private:std::vector<std::thread> workers;  // 線程向量std::queue<std::function<void()>> tasks;  // 任務隊列隊std::mutex queue<std::function<void()>> completedTasks;std::condition_variable cond;  // 條件變量bool stop;  // 停止標志void work();  // 線程工作函數
};// 構造函數初始化線程池
ThreadPool::ThreadPool(size_t threads) : stop(false) {for(size_t i = 0; i < threads; ++i)workers.emplace_back(std::thread(&ThreadPool::work, this));
}// 析構解函數等待所有線程完成
ThreadPool::~ThreadPool() {{std::unique_lock<std::mutex> lock(this->mtx);this->stop = true;}cond.notify_all();for(std::thread &worker : workers) {if(worker.joinable()) {worker.join();  // 等待線程結束}}
}// 添加任務到線程池
void ThreadPool::enqueue(std::function<void()> task) {{std::unique_lock<std::mutex> lock(mtx);if(stop)throw std::runtime_error("enqueue on stopped ThreadPool");tasks.emplace(task);}cond.notify_one();
}void ThreadPool::work() {while(true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(this->mtx);while(tasks.empty()) {cond.wait(lock);  // 如果沒有任務,等待}task = std::move(tasks.front());tasks.pop();}();task();  // 執行任務}
}

在這個示例中,ThreadPool類管理一組工作線程。構造函數創建指定數量的線程,每個線程都調用work方法等待并執行任務。enqueue方法用于添加新任務到隊列,如果線程池已經停止,則拋出異常。每個工作線程在接收到任務后會出隊列并執行它

調用帶參的構造函數

template <class Fn, class... Args>
explicit thread (Fn&& fn, Args&&... args);
  • fn:可調用對象,比如函數指針、仿函數、lambda表達式、被包裝器包裝后的可調用對象等。
  • args...:調用可調用對象fn時所需要的若干參數

調用移動構造函數

用一個右值線程對象來構造一個線程對象

void func(int n)
{for (int i = 0; i <= n; i++){cout << i << endl;}
}
int main()
{thread t3 = thread(func, 10);t3.join();return 0;
}

線程是操作系統中的一個概念,線程對象可以關聯一個線程,用來控制線程以及獲取線程的狀態。
如果創建線程對象時沒有提供線程函數,那么該線程對象實際沒有對應任何線程。
如果創建線程對象時提供了線程函數,那么就會啟動一個線程來執行這個線程函數,該線程與主線程一起運行。
thread類是防拷貝的,不允許拷貝構造和拷貝賦值,但是可以移動構造和移動賦值,可以將一個線程對象關聯線程的狀態轉移給其他線程對象,并且轉移期間不影響線程的執行

thread常用成員函數

join ,對該線程進行等待,在等待的線程返回之前,調用join函數的線程將會被阻塞
joinable , 判斷該線程是否已經執行完畢,如果是則返回true,否則返回false
detach ,將該線程與創建線程進行分離,被分離后的線程不再需要創建線程調用join函數對其進行等待
get_id , 獲取該線程的id
swap , 將兩個線程對象關聯線程的狀態進行交換

joinable函數還可以用于判定線程是否是有效的,

如果是以下任意情況,則線程無效:

采用無參構造函數構造的線程對象。(該線程對象沒有關聯任何線程)
線程對象的狀態已經轉移給其他線程對象。(已經將線程交給其他線程對象管理)
線程已經調用join或detach結束。(線程已經結束)

thread的成員函數get_id可以獲取線程的id,但該方法必須通過線程對象來調用get_id函數

如果要在線程對象關聯的線程函數中獲取線程id,調用this_thread命名空間下的get_id函數

void func()
{cout << this_thread::get_id() << endl; //獲取線程id
}
int main()
{thread t(func);t.join();return 0;
}

this_thread命名空間

yield當前線程“放棄”執行,讓操作系統調度另一線程繼續執行
sleep_until讓當前線程休眠到一個具體時間點
sleep_for讓當前線程休眠一個時間段

線程傳參

1、使用lambda

#include<thread>
#include<iostream>
#include<vector>
#include<string>int main()
{size_t n1 = 5;size_t n2 = 5;/*std::cin >> n1 >> n2;*/std::thread t1(  [n1](){for (size_t i = 0; i < n1; i++){//拿到該線程的線程idstd::cout << std::this_thread::get_id() << ":" << i << "  " << std::endl;}}   );std::thread t2([n2](){for (size_t i = 0; i < n2; i++){//拿到該線程的線程idstd::cout << std::this_thread::get_id() << ":" << i << "  " << std::endl;}} );t2.join();t1.join();return 0; 
}

join && detach

啟動一個線程后,當這個線程退出時,需要對該線程所使用的資源進行回收,否則可能會導致內存泄露等問題。thread庫提供了兩種回收線程資源的方式:

1、join

主線程創建新線程后,調用join函數等待新線程終止,當新線程終止時join函數就會自動清理線程相關的資源

join函數清理線程的相關資源后,thread對象與已銷毀的線程就沒有關系了,因此一個線程對象一般只會使用一次join,如果一個線程對象使用多次join , 程序會崩潰

void func(int n)
{for (int i = 0; i <= n; i++){cout << i << endl;}
}
int main()
{thread t(func, 20);t.join();t.join(); //程序崩潰return 0;
}

2、detach

主線程創建新線程后,也可以調用detach函數將新線程與主線程進行分離,分離后新線程會在后臺運行,其所有權和控制權將會交給C++運行庫,此時C++運行庫會保證當線程退出時,其相關資源能夠被正確回收。

使用detach的方式回收線程的資源,一般在線程對象創建好之后就立即調用detach函數。
否則線程對象可能會因為某些原因,在后續調用detach函數分離線程之前被銷毀掉,這時就會導致程序崩潰。
因為當線程對象被銷毀時會調用thread的析構函數,而在thread的析構函數中會通過joinable判斷這個線程是否需要被join,如果需要那么就會調用terminate終止當前程序(程序崩潰)

#include <iostream>
#include <thread>
#include <vector>void worker() {std::cout << "Working..." << std::endl;// 模擬耗時操作std::this_thread::sleep_for(std::chrono::seconds(2));std::cout << "Finished" << std::endl;
}int main() {std::vector<std::thread> threads;// 創建并分離多個線程for (int i = 0; i < 5; ++i) {threads.emplace_back(std::thread(worker));threads.back().detach();  // 分離線程}std::cout << "Main thread continues to run..." << std::endl;// 主線程可以繼續執行其他任務,而不需要等待分離的線程// 等待所有線程完成(可選)for (auto& th : threads) {if (th.joinable()) {th.join();}}std::cout << "All threads finished" << std::endl;return 0;
}

過度使用分離線程可能會導致資源泄露,因為分離的線程將繼續運行,即使主線程已經結束。因此,通常建議在可能的情況下使用join來管理線程的生命周期。

mutex

C++11中,mutex中總共包了四種互斥量

1、std::mute

mutex鎖是C++11提供的最基本的互斥量,mutex對象之間不能進行拷貝,也不能進行移動

mutex常用的成員函數:

lock對互斥量進行加鎖
try_lock嘗試對互斥量進行加鎖
unlock對互斥量進行解鎖,釋放互斥量的所有權

線程函數調用lock時,可能會發生以下三種情況:

1、如果該互斥量當前沒有被其他線程鎖住,則調用線程將該互斥量鎖住,直到調用unlock之前,該線程一致擁有該鎖。

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;  // 全局互斥鎖void printBlock(int n) {mtx.lock();  // 獲取鎖,如果鎖已經被其他線程占用,則等待for (int i = 0; i < n; ++i) {std::cout << "Thread " << std::this_thread::get_id() << " says " << i << std::endl;}mtx.unlock();  // 釋放鎖,其他線程可以獲取該鎖
}int main() {std::thread t1(printBlock, 1);std::thread t2(printBlock, 2);t1.join();t2.join();return 0;
}

mtx是一個全局互斥鎖,所以一次只能有一個線程執行printBlock函數。這意味著即使兩個線程幾乎同時運行,輸出也將是交錯的,而不是同時打印,因為一個線程在打印時會鎖定互斥鎖,另一個線程必須等待

2、如果該互斥量已經被其他線程鎖住,則當前的調用線程會被阻塞。

互斥鎖(mutex)在多線程環境中的基本行為。互斥鎖是一種同步機制,用于防止多個線程同時訪問共享資源,從而避免數據競爭條件和不一致的狀態。當一個線程嘗試獲取已經被其他線程持有的互斥鎖時,該線程將被阻塞,直到互斥鎖被釋放

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>std::mutex mtx;  // 全局互斥鎖void worker(int id) {// 嘗試獲取鎖mtx.lock();std::cout << "Thread " << id << " acquired the lock" << std::endl;// 模擬耗時的工作std::this_thread::sleep_for(std::chrono::milliseconds(500));std::cout << "Thread " << id << " released the lock" << std::endl;// 釋放鎖mtx.unlock();
}int main() {std::thread t1(worker, 1);std::thread t2(worker, 2);// 主線程稍作等待,以便 t1 有機會獲取鎖std::this_thread::sleep_for(std::chrono::milliseconds(100));// 嘗試在 t1 持有鎖時獲取鎖mtx.lock();std::cout << "Main thread acquired the lock" << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(100));  // 模擬主線程持有鎖mtx.unlock();t1.join();t2.join();return 0;
}

3、如果該互斥量被當前調用線程鎖住,則會產生死鎖(deadlock)。

如何使用局部變量加鎖?

例1:用lambda

int main()
{size_t n1 = 10000;size_t n2 = 10000;/*std::cin >> n1 >> n2;*/int x = 0;std::mutex mtx;std::thread t1([&n1, &x ,&mtx]() {for (size_t i = 0; i < n1; i++){mtx.lock();x++;//拿到該線程的線程id//std::cout << std::this_thread::get_id() << ":" << i << "  " << std::endl;mtx.unlock();}});std::thread t2([&n2, &x,&mtx]() {for (size_t i = 0; i < n2; i++){mtx.lock();//拿到該線程的線程idx++;//std::cout << std::this_thread::get_id() << ":" << i << "  " << std::endl;mtx.unlock();}});t2.join();t1.join();std::cout << x;return 0;
}

例2:

void Print1(size_t n,size_t  j , const std::string& s ,std::mutex & mtx) //鎖必須傳引用 ,鎖不支持拷貝
{for (size_t i = 0; i < j+n; i++){mtx.lock();std::cout << std::this_thread::get_id() << ":" << i << std::endl;mtx.unlock();}
}
int main()
{int n = 10;// 創建n個線程執行Printstd::vector<std::thread> vthd(n);std::mutex mtx;std::thread t1(Print1, 100, 1, "hello", ref(mtx ));  //必須加ref函數 ,否則鎖不能以傳引用的方式傳參傳過去std::thread t2(Print1, 100, 100000, "world", ref(mtx));t1.join();t2.join();return 0;}

例3

void Print1(size_t n, const std::string& s, std::mutex& mtx  ,int & rx) //鎖必須傳引用 ,鎖不支持拷貝
{for (size_t i = 0; i < n; i++){mtx.lock();std::cout << std::this_thread::get_id() << ":" << i << std::endl;++rx;mtx.unlock();std::this_thread::sleep_for(std::chrono::microseconds(1000));}
}int main()
{std::mutex mtx;int x = 10;std::thread t1(Print1, 5, "hello", std::ref(mtx), std::ref(x));std::thread t2(Print1,5, "world", std::ref(mtx), std::ref(x));std::cout << "線程1: " << t1.get_id() << std::endl;std::cout << "線程2: " << t2.get_id() << std::endl;t1.join();t2.join();std::cout << x << std::endl;return 0;
}

線程調用try_lock時,類似也可能會發生以下三種情況:

1、如果該互斥量當前沒有被其他線程鎖住,則調用線程將該互斥量鎖住,直到調用unlock之前,該線程一致擁有該鎖。

2、如果該互斥量已經被其他線程鎖住,則try_lock調用返回false,當前的調用線程不會被阻塞。

3、如果該互斥量被當前調用線程鎖住,則會產生死鎖(deadlock)

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

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

相關文章

List<Map<String, Object>> 如何對某個字段求和

在Java中&#xff0c;如果你有一個List<Map<String, Object>>的結構&#xff0c;并且你想要對某個特定字段進行求和&#xff0c;你可以使用Java 8的Stream API來簡化這個過程。下面是一個示例代碼&#xff0c;演示如何對某個字段進行求和。 假設你有一個List<M…

Linux 固定 IP 地址和網關

Linux 固定 IP 地址和網關 查看 IP ifconfig ifconfig eth0 ip addr ip addr show eth0 查看網關 ip route show route -n netstat -rn 設置固定 IP // 配置靜態IP文件/etc/network/interfaces $ vi /etc/network/interfacesauto eth0 iface eth0 inet static addre…

移動通信發展史

概念解釋 第一代網絡通信 1G 第二代網絡通信 2G 第三代網絡通信 3G 第四代網絡通信 4G 4g網絡有很高的速率和很低的延時——高到500M的上傳和1G的下載 日常中的4G只是用到了4G技術 運營商 移動-從民企到國企 聯通-南方教育口有人 電信 鐵通&#xff1a;成立于 2000 年…

進階數據結構——樹狀數組

前言 看這篇文章前我建議你們先看這個視頻還有這個視頻&#xff0c;不然你們可能看不懂。 一、樹狀數組的核心思想與本質 核心思想&#xff1a;樹狀數組&#xff08;Fenwick Tree&#xff09;是一種用于高效處理前綴和查詢和單點更新的數據結構。 本質&#xff1a;通過二進…

LabVIEW無刷電機控制器檢測系統

開發了一種基于LabVIEW的無刷電機控制器檢測系統。由于無刷電機具有高效率、低能耗等優點&#xff0c;在電動領域有取代傳統電機的趨勢&#xff0c;而無刷電機的核心部件無刷電機控制器產量也在不斷增長。然而&#xff0c;無刷電機控制器的出廠檢測仍處于半自動化狀態&#xff…

STM32 CAN過濾器配置和應用方法介紹

目錄 概述 一、CAN過濾器核心概念 二、過濾器配置步驟&#xff08;以標準ID為例&#xff09; 三、不同模式的配置示例 四、高級配置技巧 五、調試與問題排查 六、關鍵計算公式 總結 概述 在STM32微控制器中&#xff0c;CAN過濾器可以配置為標識符屏蔽模式和標識符列表模…

個人系統架構技術分享

架構技術 技術版本說明CentOS7.9操作系統Amoeba負責MySQL讀寫分離NFS分布式存儲ISCSI塊存儲keepalived日志收集MySQL5.7數據庫存儲MinIO8.4.5對象存儲Kubernetes1.23.15應用容器管理平臺Redis7.0分布式緩存Elasticsearch7.17.3搜索引擎nacos3.3.4服務注冊 后端技術 技術版本…

python進階篇-面向對象

1.對象的定義 1.1 什么是對象 面向過程&#xff1a;將程序流程化 對象&#xff1a;就是“容器“&#xff0c;是用來存儲數據和功能的&#xff0c;是數據和功能的集合體。 面向對象和面向過程沒有優劣之分&#xff0c;它們只是使用的場景不同罷了。 1.2 為什么要有對象 有…

網絡安全“掛圖作戰“及其場景

文章目錄 一、網絡安全掛圖作戰來源與定義1、網絡安全掛圖作戰的來源2、網絡安全掛圖作戰的定義 二、掛圖作戰關鍵技術三、掛圖作戰與傳統態勢感知的差異四、掛圖作戰主要場景五、未來趨勢結語 一、網絡安全掛圖作戰來源與定義 1、網絡安全掛圖作戰的來源 網絡安全掛圖作戰的…

【嵌入式Linux應用開發基礎】read函數與write函數

目錄 一、read 函數 1.1. 函數原型 1.2. 參數說明 1.3. 返回值 1.4. 示例代碼 二、write 函數 2.1. 函數原型 2.2. 參數說明 2.3. 返回值 2.4. 示例代碼 三、關鍵注意事項 3.1 部分讀寫 3.2 錯誤處理 3.3 阻塞與非阻塞模式 3.4 數據持久化 3.5 線程安全 四、嵌…

嵌入式八股文(四)計算機網絡篇

第一章 基礎概念 1. 服務 指網絡中各層為緊鄰的上層提供的功能調用,是垂直的。包括面向連接服務、無連接服務、可靠服務、不可靠服務。 2. 協議 是計算機?絡相互通信的對等層實體之間交換信息時必須遵守的規則或約定的集合。?絡協議的三個基本要素:語法、…

LabVIEW 天然氣水合物電聲聯合探測

天然氣水合物被認為是潛在的清潔能源&#xff0c;其儲量豐富&#xff0c;預計將在未來能源格局中扮演重要角色。由于其獨特的物理化學特性&#xff0c;天然氣水合物的探測面臨諸多挑戰&#xff0c;涉及溫度、壓力、電學信號、聲學信號等多個參數。傳統的人工操作方式不僅效率低…

JAVA代碼走查重構常用prompt

代碼重構prompt&#xff1a; ## 主題&#xff1a; 代碼重構 ## 角色扮演: 你是軟件開發大師Martin Fowler&#xff0c;精通代碼重構、面向對象編程、Clean Code和設計模式&#xff0c;且熟練掌握《重構&#xff0c;改善既有代碼的設計》這本書中的重構思想和各種重構方法。 ## …

[數據結構]紅黑樹,詳細圖解插入

目錄 一、紅黑樹的概念 二、紅黑樹的性質 三、紅黑樹節點的定義 四、紅黑樹的插入&#xff08;步驟&#xff09; 1.為什么新插入的節點必須給紅色&#xff1f; 2、插入紅色節點后&#xff0c;判定紅黑樹性質是否被破壞 五、插入出現連續紅節點情況分析圖解&#xff08;看…

STM32 HAL庫USART串口DMA IDLE中斷編程:避坑指南

HAL_UART_Receive接收最容易丟數據了,STM32 HAL庫UART查詢方式實例 可以考慮用中斷來實現,但是HAL_UART_Receive_IT還不能直接用,容易數據丟失,實際工作中不會這樣用,STM32 HAL庫USART串口中斷編程&#xff1a;演示數據丟失, 需要在此基礎優化一下. STM32F103 HAL庫USART串口…

sql注入中information_schema被過濾的問題

目錄 一、information_schema庫的作用 二、獲得表名 2.1 sys.schema_auto_increment_columns 2.2 schema_table_statistics 三、獲得列名 join … using … order by盲注 子查詢 在進行sql注入時&#xff0c;我們經常會使用information_schema來進行爆數據庫名、表名、…

Jenkins 給任務分配 節點(Node)、設置工作空間目錄

Jenkins 給任務分配 節點(Node)、設置工作空間目錄 創建 Freestyle project 類型 任務 任務配置 Node 打開任務-> Configure-> General 勾選 Restrict where this project can be run Label Expression 填寫一個 Node 的 Label&#xff0c;輸入有效的 Label名字&#x…

Electron:使用electron-react-boilerplate創建一個react + electron的項目

使用 electron-react-boilerplate git clone --depth 1 --branch main https://github.com/electron-react-boilerplate/electron-react-boilerplate.git your-project-name cd your-project-name npm install npm start 安裝不成功 在根目錄加上 .npmrc文件 內容為 electron_…

數控機床設備分布式健康監測與智能維護系統MTAgent

數控機床設備分布式健康監測與智能維護系統MTAgent-v1.1融合了目前各種先進的信號處理以及信息分析算法以算法工具箱的方式&#xff0c;采用了一種開發的、模塊化的結構實現信號各種分析處理&#xff0c;采用Python編程語言&#xff0c;滿足不同平臺需求(包括Windows、Linux)。…

FPGA VIVADO:axi-lite 從機和主機

FPGA VIVADO:axi-lite 從機和主機 TOC在這里插入代碼片 前言 協議就不詳細講解了&#xff0c;直接看手冊即可。下面主要如何寫代碼和關鍵的時序。 此外下面的代碼可以直接用于實際工程 一、AXI-LITE 主機 數據轉axi lite接口&#xff1a; 讀/寫數據FIFO緩存 仲裁&#xff1a…