日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

主线程是如何向子线程传递数据的?_c++ 利用thread创建线程

發(fā)布時(shí)間:2025/3/11 c/c++ 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 主线程是如何向子线程传递数据的?_c++ 利用thread创建线程 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

用進(jìn)行多線程開(kāi)發(fā)

小時(shí)候,老師總是教育我們上課要專(zhuān)心,“一心不可二用”。可是CPU這個(gè)不聽(tīng)話的“熊孩子”偏偏卻在一個(gè)芯片中加入了兩個(gè)甚至多個(gè)運(yùn)算核心,想要一“芯”二用。從硬件廠商的角度,通過(guò)增加CPU的運(yùn)算核心,突破了原來(lái)單核CPU的頻率極限,確實(shí)可以很大程度上增加CPU的總頻率。在他們看來(lái),這簡(jiǎn)直就是一個(gè)天才的創(chuàng)意。可是從軟件廠商的角度,CPU運(yùn)算核心的增加,并沒(méi)有顯著地提高軟件的性能表現(xiàn),有時(shí)候甚至?xí)档蛙浖男阅堋T谒麄兛磥?lái),這無(wú)疑是一場(chǎng)噩夢(mèng)的開(kāi)始。

在以往的計(jì)算機(jī)發(fā)展歷史中,硬件技術(shù)的發(fā)展,特別是CPU頻率的不斷提升,總是同時(shí)會(huì)提升軟件的性能。更重要的是,這種性能的提升是完全免費(fèi)的,軟件什么都不需要做,只要換上新出來(lái)的更高頻率的CPU,軟件就獲得更好的性能表現(xiàn)。從286到486,從奔騰到酷睿,每次CPU頻率的提升,無(wú)不給軟件性能帶來(lái)大幅提升。如果有用戶抱怨我們的軟件性能不佳,我們也無(wú)須著急,只需要坐等Intel或者AMD推出更高頻率的CPU并讓用戶換上就可以了。

但是,當(dāng)CPU的發(fā)展進(jìn)入多核時(shí)代以后,程序員們沮喪地發(fā)現(xiàn),CPU長(zhǎng)期提供的這份“免費(fèi)的午餐”消失了。這是因?yàn)楫?dāng)前大多數(shù)程序幾乎都是針對(duì)一個(gè)運(yùn)算核心的CPU而設(shè)計(jì)的單線程程序。雖然CPU有多個(gè)運(yùn)算核心,但它卻只能在其中的某一個(gè)運(yùn)算核心上進(jìn)行運(yùn)算,而其他的運(yùn)算核心并沒(méi)有得到利用而白白浪費(fèi)了。雖然CPU的運(yùn)算核心增加了,總的頻率增加了,但是單個(gè)運(yùn)算核心的頻率并沒(méi)有太大變化,所以程序的性能并沒(méi)有隨著CPU運(yùn)算核心的增加而得到顯著的提升。

從單核CPU到多核CPU

浪費(fèi)是可恥的,更何況我們浪費(fèi)的是如此寶貴的CPU運(yùn)算資源。為了把被浪費(fèi)的CPU運(yùn)算資源充分地利用起來(lái),唯一的辦法就是針對(duì)CPU的多個(gè)運(yùn)算核心設(shè)計(jì)我們的程序,通過(guò)將原來(lái)由單個(gè)線程串行執(zhí)行的程序并行化,用多線程代替原來(lái)的單線程,使其可以同時(shí)運(yùn)行在多個(gè)運(yùn)算核心上,以此來(lái)實(shí)現(xiàn)對(duì)CPU多個(gè)運(yùn)算核心的充分利用。這樣,程序的性能又會(huì)隨著CPU頻率的提高而得到提升。

可是,多線程程序的設(shè)計(jì)并不是一個(gè)輕松簡(jiǎn)單的活兒。在C++11之前,如果我們想要實(shí)現(xiàn)一個(gè)多線程程序,我們需要使用系統(tǒng)API創(chuàng)建線程,需要小心地維護(hù)對(duì)共享資源的訪問(wèn)等等。更折磨人的是,多線程增加了程序的設(shè)計(jì)與實(shí)現(xiàn)難度,如果設(shè)計(jì)錯(cuò)誤,多線程不僅不會(huì)提升程序的性能,反而可能會(huì)降低性能,甚至引起資源互鎖而導(dǎo)致程序失去響應(yīng)。為了簡(jiǎn)化多線程程序的設(shè)計(jì)與實(shí)現(xiàn),C++11的標(biāo)準(zhǔn)庫(kù)專(zhuān)門(mén)提供了頭文件以支持多線程程序的開(kāi)發(fā),而那份美味的“免費(fèi)的午餐”也正在回到我們面前。

利用thread創(chuàng)建線程

C++11中的頭文件提供了thread、mutex以及unique_lock等基本對(duì)象來(lái)對(duì)多線程開(kāi)發(fā)中最常用的線程、互斥以及鎖等基本概念進(jìn)行抽象與表達(dá),為多線程程序的實(shí)現(xiàn)提供了一個(gè)較低抽象層次的編程模型。其中,最基礎(chǔ)也最重要的是與線程概念相對(duì)應(yīng)的thread類(lèi)。線程是對(duì)程序中的某個(gè)執(zhí)行或者計(jì)算過(guò)程的一種表述,而所謂的多線程程序,就是將原來(lái)的一個(gè)執(zhí)行過(guò)程分成多個(gè)過(guò)程去執(zhí)行。由此可見(jiàn),線程的創(chuàng)建,是實(shí)現(xiàn)多線程的基礎(chǔ)。可是在以往,要想在程序中創(chuàng)建一個(gè)線程,我們不得不針對(duì)不同的操作系統(tǒng)調(diào)用不同的系統(tǒng)函數(shù),然后還要提供復(fù)雜的參數(shù)才能完成線程的創(chuàng)建。幸運(yùn)的是,thread類(lèi)的出現(xiàn),大大地簡(jiǎn)化了這一過(guò)程。

thread類(lèi)對(duì)線程概念進(jìn)行了很好的抽象與實(shí)現(xiàn),從而使得我們可以非常簡(jiǎn)單地使用一個(gè)函數(shù)指針(也包括函數(shù)對(duì)象和Lambda表達(dá)式)來(lái)構(gòu)建一個(gè)thread對(duì)象。而一旦擁有了thread對(duì)象,就意味著我們創(chuàng)建了一個(gè)線程,也就可以利用thread對(duì)象所提供的成員函數(shù)對(duì)這個(gè)線程進(jìn)行調(diào)度,啟動(dòng)、掛起或者停止這個(gè)線程,以操作線程完成某個(gè)執(zhí)行過(guò)程。例如:

#include // 引入定義thread類(lèi)的頭文件#include // 使用thread所在的名字空間using namespace std; // 定義需要線程執(zhí)行的函數(shù)和函數(shù)對(duì)象void ListenMusic(){cout<

在這段代碼中,我們利用函數(shù)對(duì)象類(lèi)ReadBook的一個(gè)read函數(shù)對(duì)象和指向ListenMusic()函數(shù)的函數(shù)指針(也就是它的函數(shù)名)作為thread類(lèi)構(gòu)造函數(shù)的參數(shù),分別創(chuàng)建了readthread和listenthread這兩個(gè)thread對(duì)象。thread對(duì)象的創(chuàng)建,意味著它將創(chuàng)建新的線程,并開(kāi)始執(zhí)行作為構(gòu)造函數(shù)參數(shù)傳遞給thread對(duì)象的函數(shù)對(duì)象或者是函數(shù),通過(guò)簡(jiǎn)單的一個(gè)步驟,就完成了線程的創(chuàng)建與啟動(dòng)執(zhí)行。在多線程環(huán)境下,我們將執(zhí)行主函數(shù)并負(fù)責(zé)創(chuàng)建其它線程的線程稱(chēng)為主線程,而那些被創(chuàng)建的線程則相應(yīng)地被稱(chēng)為分支線程或工作者線程,其執(zhí)行的函數(shù)則被稱(chēng)為線程函數(shù)。在執(zhí)行的時(shí)候,主線程開(kāi)始進(jìn)入主函數(shù)執(zhí)行,通過(guò)創(chuàng)建兩個(gè)thread對(duì)象而創(chuàng)建了兩個(gè)分支線程并立即啟動(dòng)執(zhí)行其線程函數(shù),而與此同時(shí),執(zhí)行主函數(shù)的主線程將繼續(xù)向下執(zhí)行。這樣,主線程和兩個(gè)分支線程同時(shí)都在執(zhí)行,操作系統(tǒng)會(huì)將它們調(diào)度到CPU的多個(gè)運(yùn)算核心去執(zhí)行,以此達(dá)到對(duì)CPU多個(gè)運(yùn)算核心的充分利用。當(dāng)主線程遇到thread對(duì)象調(diào)用的join()函數(shù)后,主線程將等待這個(gè)thread對(duì)象執(zhí)行完畢之后,再繼續(xù)往下執(zhí)行,直到最后主函數(shù)執(zhí)行完畢,退出整個(gè)程序。整個(gè)程序的執(zhí)行流程如下圖12-4所示:

多線程程序的執(zhí)行流程

除了利用thread對(duì)象創(chuàng)建新的線程簡(jiǎn)單地執(zhí)行某個(gè)線程函數(shù)之外,就像我們常常需要通過(guò)參數(shù)與普通函數(shù)進(jìn)行數(shù)據(jù)傳遞一樣,更多時(shí)候,我們也需要向線程函數(shù)內(nèi)傳入數(shù)據(jù)以供其進(jìn)行處理,或者是從線程函數(shù)中傳出結(jié)果數(shù)據(jù)。要做到這一點(diǎn),我們同樣需要給線程函數(shù)加上參數(shù),跟普通函數(shù)類(lèi)似,如果只是需要向線程函數(shù)內(nèi)傳入數(shù)據(jù),那就加上傳值形式的參數(shù),而如果加上傳指針和傳引用形式的參數(shù),則既可以傳入也可以傳出數(shù)據(jù)。與普通函數(shù)在調(diào)用時(shí)將實(shí)際參數(shù)復(fù)制給函數(shù)的形式參數(shù)所不同的是,線程函數(shù)的形式參數(shù)的賦值是在這個(gè)函數(shù)被用于創(chuàng)建thread對(duì)象時(shí)完成的。當(dāng)我們?cè)谑褂媚硞€(gè)帶有參數(shù)的線程函數(shù)創(chuàng)建thread對(duì)象時(shí),在thread構(gòu)造函數(shù)的實(shí)際參數(shù)中,我們不僅要用這個(gè)函數(shù)指針或者函數(shù)對(duì)象做第一個(gè)參數(shù),同時(shí)其后還要依次加上線程函數(shù)所需要的各個(gè)實(shí)際參數(shù)。在創(chuàng)建thread對(duì)象的時(shí)候,這些實(shí)際參數(shù)會(huì)被拷貝復(fù)制給線程函數(shù)相應(yīng)的形式參數(shù),以此來(lái)實(shí)現(xiàn)數(shù)據(jù)的傳遞。這里需要注意的是,如果線程函數(shù)的參數(shù)是傳引用形式,那么在創(chuàng)建thread對(duì)象的時(shí)候,我們需要使用ref()函數(shù)獲得實(shí)際參數(shù)的引用才行,否則,即使這個(gè)參數(shù)是引用形式,它也會(huì)被拷貝復(fù)制而在線程函數(shù)和本地函數(shù)間形成兩個(gè)副本,起不到傳出數(shù)據(jù)的效果。例如,我們需要向上面的ListenMusic()線程函數(shù)傳入歌曲名并從中傳出結(jié)果數(shù)據(jù):

// 需要傳遞數(shù)據(jù)的線程函數(shù)// 傳值形式的strSong負(fù)責(zé)向線程函數(shù)內(nèi)傳入數(shù)據(jù)// 傳引用形式的vecEar負(fù)責(zé)向線程函數(shù)外傳出數(shù)據(jù)void ListenMusic(string strSong,vector& vecEar){ cout< vecEar; // 用于保存結(jié)果數(shù)據(jù)的容器 string strSong = "歌唱祖國(guó)"; // 傳入線程函數(shù)的數(shù)據(jù) // 在創(chuàng)建thread對(duì)象時(shí)傳遞數(shù)據(jù)// 第一個(gè)參數(shù)是線程函數(shù)指針,其后依次是線程函數(shù)所需要的參數(shù)thread listenthread(ListenMusic,strSong,ref(vecEar)); // …listenthread.join(); // 輸出結(jié)果數(shù)據(jù) for(string strName : vecEar) cout<

利用thread對(duì)象,我們可以像調(diào)用普通函數(shù)一樣簡(jiǎn)單地用thread對(duì)象創(chuàng)建另外一個(gè)線程來(lái)執(zhí)行我們的線程函數(shù),也可以像與普通函數(shù)傳遞數(shù)據(jù)一樣簡(jiǎn)單地與線程函數(shù)傳遞數(shù)據(jù),從此,一邊聽(tīng)著歌還可以一邊看著書(shū),輕松開(kāi)啟我們愜意的“一芯二用”的并行生活。

知道更多:線程中的瞌睡蟲(chóng)

在利用線程執(zhí)行某個(gè)任務(wù)的時(shí)候,我們往往要對(duì)線程的執(zhí)行時(shí)間進(jìn)行控制,讓線程在等待一定時(shí)間之后再繼續(xù)執(zhí)行,或者是在某個(gè)事先設(shè)定的固定時(shí)間點(diǎn)之后執(zhí)行。這時(shí),我們就需要用到std::this_thread名字空間下的sleep_for()函數(shù)和sleep_until()函數(shù)來(lái)完成對(duì)線程執(zhí)行狀態(tài)的時(shí)間控制了。

sleep_for()函數(shù)可以讓當(dāng)前線程(也就是調(diào)用這個(gè)函數(shù)的線程)暫停執(zhí)行一段時(shí)間,等過(guò)了這段時(shí)間之后再繼續(xù)恢復(fù)執(zhí)行;而sleep_until()函數(shù)則是讓當(dāng)前線程一直暫停,直到某個(gè)固定時(shí)間點(diǎn)的到來(lái)才會(huì)繼續(xù)恢復(fù)執(zhí)行。它們就像兩條瞌睡蟲(chóng),一條可以讓線程瞌睡一整天(固定時(shí)間段),而另一條更厲害,可以讓線程一直瞌睡到天明(固定時(shí)間點(diǎn))。對(duì)于我們來(lái)說(shuō),瞌睡蟲(chóng)很是討厭,可是對(duì)于線程來(lái)說(shuō),瞌睡蟲(chóng)卻是大有用處。

比如,我們想要模擬一下傳說(shuō)中的2012世界末日,就需要這兩條瞌睡蟲(chóng)來(lái)讓線程瞌睡瞌睡:

#include #include // 引入線程相關(guān)的頭文件#include // 引入時(shí)間相關(guān)的頭文件 using namespace std;using namespace std::chrono; // 使用時(shí)間相關(guān)的名字空間 int main(){// 構(gòu)造一個(gè)固定時(shí)間點(diǎn):2012年 12月21日零時(shí) tm timeinfo = tm(); timeinfo.tm_year = 112; // 年: 2012 = 1900 + 112timeinfo.tm_mon = 11; // 月:12 = 1 + 11 timeinfo.tm_mday = 21; // 21日time_t tt = mktime(&timeinfo);// 利用time_t類(lèi)型的變量tt創(chuàng)建一個(gè)表示世界末日固定時(shí)間點(diǎn)的time_point對(duì)象tp system_clock::time_point tp = system_clock::from_time_t (tt); // 當(dāng)前線程一直瞌睡到tp表示的2012年12月21日零時(shí)this_thread::sleep_until(tp); // 世界末日到了,程序繼續(xù)恢復(fù)執(zhí)行,響鈴10次發(fā)出警報(bào)for(int i = 0; i < 10; ++i){cout<

在兩條瞌睡蟲(chóng)的合作下,我們的模擬程序會(huì)首先在sleep_until()的作用下,瞌睡(暫停執(zhí)行)到tp所表示的固定時(shí)間點(diǎn)(2012年12月21日零時(shí)),等到了這個(gè)時(shí)間點(diǎn)后,程序才會(huì)恢復(fù)執(zhí)行。緊接著,程序執(zhí)行進(jìn)入一個(gè)for循環(huán),每次循環(huán),它都會(huì)輸出一個(gè)計(jì)算機(jī)響鈴,然后又會(huì)在sleep_for()這條瞌睡蟲(chóng)的作用下,休眠一秒鐘,然后再繼續(xù)執(zhí)行下一次循環(huán)。整個(gè)程序的效果就是到了世界末日,這個(gè)程序會(huì)發(fā)出滴滴滴的警報(bào),告訴我們,世界末日來(lái)了,趕緊逃命吧!可是,可是,2012早都過(guò)去了,雖然世界末日沒(méi)來(lái),可這個(gè)程序卻忠實(shí)地準(zhǔn)時(shí)發(fā)出了警報(bào)。由此可見(jiàn),瑪雅人忽悠人,還是C++程序更可信。

總結(jié)

以上是生活随笔為你收集整理的主线程是如何向子线程传递数据的?_c++ 利用thread创建线程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。