深入解析C++中的std::thread的使用
std::thread簡(jiǎn)介
C++11之前,window和linux平臺(tái)分別有各自的多線程標(biāo)準(zhǔn),使用C++編寫(xiě)的多線程往往是依賴于特定平臺(tái)的。
- Window平臺(tái)提供用于多線程創(chuàng)建和管理的win32 api;
- Linux下則有POSIX多線程標(biāo)準(zhǔn),Threads或Pthreads庫(kù)提供的API可以在類Unix上運(yùn)行;
在C++11新標(biāo)準(zhǔn)中,可以簡(jiǎn)單通過(guò)使用thread庫(kù),來(lái)管理多線程。thread庫(kù)可以看做對(duì)不同平臺(tái)多線程API的一層包裝;因此使用新標(biāo)準(zhǔn)提供的線程庫(kù)編寫(xiě)的程序是跨平臺(tái)的。
一、C++11 線程創(chuàng)建
- 每一個(gè) C++11 程序都包含一個(gè)主線程即 main() 函數(shù),在 C++11 中可以通過(guò)創(chuàng)建 std::thread 對(duì)象來(lái)創(chuàng)建新的線程,每個(gè) std::thread 對(duì)象都可以與一個(gè)線程相關(guān)聯(lián)。
- 需要引用的頭文件:
#include <thread>
二、std::thread 的構(gòu)造函數(shù)中接收什么參數(shù)?
- 可以給 std::thread 對(duì)象添加函數(shù),這個(gè)回調(diào)函數(shù)將在這個(gè)新線程啟動(dòng)時(shí)執(zhí)行。這些回調(diào)可以是:
- 函數(shù)指針;
- 函數(shù)對(duì)象;
- Lambda 函數(shù)。
- 創(chuàng)建 thread 對(duì)象:
std::thread thObj(<CALLBACK>);
- 新線程將在創(chuàng)建新對(duì)象后立即啟動(dòng),并將并行地執(zhí)行(當(dāng)參數(shù))傳遞給線程的回調(diào)函數(shù)。此外,任何線程都可以通過(guò)調(diào)用某線程對(duì)象上的 join( ) 函數(shù)來(lái)等待此線程退出。
- 來(lái)看一個(gè)例子,主線程將創(chuàng)建另外一個(gè)線程,創(chuàng)建這個(gè)新線程后,主線程會(huì)在控制臺(tái)上打印一些數(shù)據(jù),然后等待新創(chuàng)建的線程退出。
- 使用函數(shù)指針創(chuàng)建線程:
#include <thread> void thread_function() { for(int i = 0; i < 10000; i++); std::cout<<"thread function Executing"<<std::endl; } int main() { std::thread threadObj(thread_function); for(int i = 0; i < 10000; i++); std::cout<<"Display From MainThread"<<std::endl; threadObj.join(); std::cout<<"Exit of Main function"<<std::endl; return 0; }
- 使用函數(shù)對(duì)象創(chuàng)建線程:
#include <iostream> #include <thread> class DisplayThread { public: void operator()() { for(int i = 0; i < 10000; i++) std::cout<<"Display Thread Executing"<<std::endl; } }; int main() { std::thread threadObj( (DisplayThread()) ); for(int i = 0; i < 10000; i++) std::cout<<"Display From Main Thread "<<std::endl; std::cout<<"Waiting For Thread to complete"<<std::endl; threadObj.join(); std::cout<<"Exiting from Main Thread"<<std::endl; return 0; }
- 使用 Lambda 函數(shù)創(chuàng)建線程:
#include <iostream> #include <thread> int main() { int x = 9; std::thread threadObj([]{ for(int i = 0; i < 10000; i++) std::cout<<"Display Thread Executing"<<std::endl; }); for(int i = 0; i < 10000; i++) std::cout<<"Display From Main Thread"<<std::endl; threadObj.join(); std::cout<<"Exiting from Main Thread"<<std::endl; return 0; }
- 如何區(qū)分線程:
- 每個(gè) std::thread 對(duì)象都有一個(gè) ID,使用下面的函數(shù)可以獲?。?/li>
std::thread::get_id()
獲取當(dāng)前線程的 ID:
std::this_thread::get_id()
- 如果 std::thread 對(duì)象沒(méi)有和任何對(duì)象關(guān)聯(lián),則 get_id() 函數(shù)會(huì)返回默認(rèn)構(gòu)造的 std::thread::id 對(duì)象,即“非線程”。std::thread::id 是一個(gè)對(duì)象,它也可以在控制臺(tái)上進(jìn)行比較和打?。?/li>
#include <iostream> #include <thread> void thread_function() { std::cout<<"Inside Thread :: ID = "<<std::this_thread::get_id()<<std::endl; } int main() { std::thread threadObj1(thread_function); std::thread threadObj2(thread_function); if(threadObj1.get_id() != threadObj2.get_id()) std::cout<<"Both Threads have different IDs"<<std::endl; std::cout<<"From Main Thread :: ID of Thread 1 = "<<threadObj1.get_id()<<std::endl; std::cout<<"From Main Thread :: ID of Thread 2 = "<<threadObj2.get_id()<<std::endl; threadObj1.join(); threadObj2.join(); return 0; }
三、std::thread 的搭配用法
① std::promise
- 為了在不同的線程之間傳遞數(shù)據(jù),C++ 引入了 std::promise 和 std::future 這兩種數(shù)據(jù)結(jié)構(gòu),在頭文件 <future> 中包含。
- promise 是一個(gè)范型的數(shù)據(jù)結(jié)構(gòu),你可以定義一個(gè)整形的 promise:promise,這意味著線程之間傳遞的值是整形。promise 的 get_future() 方法返回一個(gè) future 數(shù)據(jù)結(jié)構(gòu),從這個(gè) future 數(shù)據(jù)結(jié)構(gòu)可以獲取設(shè)置給 promise 的值:
#include <iostream> #include <future> #include <thread> using namespace std; int main() { promise<int> a_promise; auto a_future = a_promise.get_future(); a_promise.set_value(10); cout << a_future.get() << endl; cout << "after get()" << endl; return 0; }
輸出結(jié)構(gòu)是:
10
after get()
- 實(shí)際上,上面的例子并沒(méi)有使用線程,但是很好得展示了 promise 和 future 之間的關(guān)系。更復(fù)雜一點(diǎn)的使用場(chǎng)景可能如下:
- 主線程定義一個(gè) promise,命名為 p;
- 主線程調(diào)用 p.get_future(),并把返回值保存為引用 f;
- 主線程啟動(dòng)一個(gè)子線程,并把 p 作為啟動(dòng)參數(shù)傳給子線程;
- 主線程調(diào)用 f.get(),但是此時(shí)子線程還未將數(shù)據(jù)放入 promise 內(nèi),所以主線程掛起;
- 子線程執(zhí)行完,獲取到結(jié)果,并把結(jié)果寫(xiě)入 p;
- 主線程從 f.get() 的調(diào)用中被喚醒,獲取到子線程寫(xiě)入 p 的值,繼續(xù)執(zhí)行。
② std::packaged_task
C++11 很貼心地提供 packaged_task 類型,可以不用直接使用 std::thread 和 std::promise,直接就能夠生成線程,派遣任務(wù):
#include <iostream> #include <future> using namespace std; int f() { string hi = "hello, world!"; cout << hi << endl; return hi.size(); } int main() { packaged_task<int ()> task(f); auto result = task.get_future(); task(); cout << result.get() << endl; return 0; }
運(yùn)行結(jié)果為:
hello, world!
13
③ std::async
- std::packaged_task 要求自己?jiǎn)?dòng)任務(wù),比如要顯示調(diào)用 task(),如果連這一步都想省了的話,可以使用 std:async:
#include <iostream> #include <vector> #include <algorithm> #include <numeric> #include <future> template <typename RandomIt> int parallel_sum(RandomIt beg, RandomIt end) { auto len = end - beg; if (len < 1000) return std::accumulate(beg, end, 0); RandomIt mid = beg + len/2; auto handle = std::async(std::launch::async, parallel_sum<RandomIt>, mid, end); int sum = parallel_sum(beg, mid); return sum + handle.get(); } int main() { std::vector<int> v(10000, 1); std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n'; }
運(yùn)行結(jié)果:
The sum is 10000
④ std::this_thread
- C++11 專門(mén)提供了一個(gè)命名空間 std::this_thread 來(lái)表示當(dāng)前線程。
- std::this_thread 提供了幾個(gè)方法可以對(duì)線程做一定的控制:
- get_id(),獲取線程 id;
- yield(),釋放執(zhí)行權(quán);
- sleep_for(),使線程沉睡一定時(shí)間。
#include <iostream> #include <future> #include <thread> using namespace std; int f(promise<int> my_promise) { string hi = "hello, world!"; my_promise.set_value(hi.size()); this_thread::sleep_for(0.1s); cout << hi << endl; } int main() { promise<int> f_promise; auto result = f_promise.get_future(); thread f_thread(f, move(f_promise)); cout << result.get() << endl; f_thread.join(); return 0; }
到此這篇關(guān)于深入解析C++中的std::thread的使用的文章就介紹到這了,更多相關(guān)C++ std::thread使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言中的字符型數(shù)據(jù)與ASCII碼表
這篇文章主要介紹了C語(yǔ)言中的字符型數(shù)據(jù)與ASCII碼表,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01C語(yǔ)言之函數(shù)遞歸的實(shí)現(xiàn)
本文主要介紹了C語(yǔ)言之函數(shù)遞歸的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07C++ 數(shù)據(jù)結(jié)構(gòu)二叉樹(shù)(前序/中序/后序遞歸、非遞歸遍歷)
這篇文章主要介紹了C++ 數(shù)據(jù)結(jié)構(gòu)二叉樹(shù)(前序/中序/后序遞歸、非遞歸遍歷)的相關(guān)資料,這里提供實(shí)例代碼來(lái)幫助大家理解掌握二叉樹(shù),需要的朋友可以參考下2017-07-07Qt基礎(chǔ)開(kāi)發(fā)之Qt多線程類QThread與Qt定時(shí)器類QTimer的詳細(xì)方法與實(shí)例
這篇文章主要介紹了Qt基礎(chǔ)開(kāi)發(fā)之Qt多線程類QThread與Qt定時(shí)器類QTimer的詳細(xì)方法與實(shí)例,需要的朋友可以參考下2020-03-03C++?select模型簡(jiǎn)單聊天室的實(shí)現(xiàn)示例
本文主要介紹了C++?select模型簡(jiǎn)單聊天室的實(shí)現(xiàn)示例,使用CMake項(xiàng)目進(jìn)行開(kāi)發(fā),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05C語(yǔ)言判斷字符串長(zhǎng)度的方法小結(jié)
學(xué)過(guò)C/C++的人都知道,在C/C++中并沒(méi)有提供直接獲取數(shù)組長(zhǎng)度的函數(shù),對(duì)于存放字符串的字符數(shù)組提供了一個(gè)strlen函數(shù)獲取其長(zhǎng)度,那么對(duì)于其他類型的數(shù)組如何獲取他們的長(zhǎng)度呢?本文給大家介紹了C語(yǔ)言判斷字符串長(zhǎng)度的方法小結(jié),需要的朋友可以參考下2024-08-08