C++11 ,封裝了thread的多線(xiàn)程的類(lèi),這樣對(duì)多線(xiàn)程的使用更加方便。
多線(xiàn)程的原理我不加贅述,可以參看操作系統(tǒng)等參考書(shū)。
多線(xiàn)程代碼可以最大化利用計(jì)算機(jī)性能資源,提高代碼的運(yùn)行效率,是常用優(yōu)化方法。
我不是C++大神,初學(xué)階段的菜鳥(niǎo)而已,很多問(wèn)題我還是不理解當(dāng)中的原理,寫(xiě)這篇博客的原因,也是記錄自己的學(xué)習(xí)心得和思路,供自己日后自己思考。
首先從簡(jiǎn)單的問(wèn)題入手,如何寫(xiě)一個(gè)多線(xiàn)程的C++代碼?
#include<iostream>#include<thread>void fun(int a){ a++;}int main(){ int a=0; std::thread t(fun,a); //創(chuàng)建一個(gè)線(xiàn)程t,t調(diào)用函數(shù)fun,a作為fun的參數(shù),也要寫(xiě)到thread的構(gòu)造函數(shù)當(dāng)中;
t.join(); //啟動(dòng)線(xiàn)程t,并且阻塞主線(xiàn)程,等到線(xiàn)程t運(yùn)行結(jié)束后,再繼續(xù)運(yùn)行主線(xiàn)程; std::cout<<a<<std::endl;}
上面這段代碼是最簡(jiǎn)單的多線(xiàn)程代碼,調(diào)用thread類(lèi),并利用了thread的構(gòu)造函數(shù)創(chuàng)建一個(gè)線(xiàn)程t,thread類(lèi)的構(gòu)造函數(shù)重載了很多,后面會(huì)繼續(xù)說(shuō)到。
在這里要說(shuō)一下,thread類(lèi)當(dāng)中的兩個(gè)成員函數(shù),join()和detach()。這兩個(gè)成員的作用就像上面代碼的注釋那樣,啟動(dòng)新生成的線(xiàn)程的,但是區(qū)別在于join()函數(shù)是啟動(dòng)子線(xiàn)程而阻塞主線(xiàn)程,當(dāng)子線(xiàn)程運(yùn)行結(jié)束后,才會(huì)繼續(xù)運(yùn)行主線(xiàn)程。相比之下,detach()函數(shù)的作用是啟動(dòng)子線(xiàn)程,并且讓子線(xiàn)程和主線(xiàn)程分離,子線(xiàn)程和主線(xiàn)程各運(yùn)行各的,雖然兩個(gè)線(xiàn)程會(huì)因?yàn)楣蚕韮?nèi)存池的原因在操作系統(tǒng)的層面發(fā)生發(fā)生阻塞等關(guān)系,但是在代碼層次上,兩個(gè)線(xiàn)程并不存在誰(shuí)阻塞誰(shuí),很可能主線(xiàn)程已經(jīng)運(yùn)行結(jié)束了,子線(xiàn)程還在運(yùn)行。
接下來(lái),我們要說(shuō)一下類(lèi)當(dāng)中的成員函數(shù)如何初始化thread類(lèi)的構(gòu)造函數(shù)。
對(duì)于類(lèi)的成員函數(shù),我們需要給出類(lèi)對(duì)象的地址:
#include<iostream>#include<thread>class A{public: void fun(int a,int b){ std::cout<<"this is A thread!"<<a<<std::endl; }};int main(){ int k=0; A a; std::thread t(&A::fun,a,k,k+1); t.join();}
std::thread t(&A::fun,a,k,k+1); 這個(gè)地方就可以看出thread類(lèi)的構(gòu)造對(duì)于成員函數(shù)的重載了,std::thread t(函數(shù)(成員函數(shù))地址,對(duì)象地址,成員函數(shù)的參數(shù)1,參數(shù)2,參數(shù)3...)。
相比非成員函數(shù),成員函數(shù)需要給出類(lèi)實(shí)例化對(duì)象的地址,如果該線(xiàn)程是在同一類(lèi)的某一成員函數(shù)當(dāng)中被構(gòu)造,則直接用this關(guān)鍵字代替即可。
其實(shí),我在寫(xiě)成員函數(shù)的多線(xiàn)程代碼的時(shí)候,發(fā)現(xiàn)成員函數(shù)的需要傳遞的參數(shù)太多會(huì)使thread類(lèi)的構(gòu)造函數(shù)重載失敗,我測(cè)試了一下,成員函數(shù)最多只能傳遞4個(gè)參數(shù),也就說(shuō)std::thread類(lèi)的構(gòu)造函數(shù)最多只能重載6個(gè)參數(shù)。
這一點(diǎn),我并沒(méi)有找到相關(guān)文檔得到證實(shí),只是在寫(xiě)代碼的時(shí)候發(fā)現(xiàn)成員函數(shù)傳遞參數(shù)太多,會(huì)一直編譯不通過(guò),偶然間發(fā)現(xiàn)這個(gè)點(diǎn)的,具體到底對(duì)不對(duì),我也不是很確定。
其次,我們要說(shuō)一下加鎖和解鎖的問(wèn)題。
因?yàn)槲覀儎?chuàng)造的每一個(gè)線(xiàn)程只要在一個(gè)進(jìn)程內(nèi),都是共享內(nèi)存池的,這樣在讀寫(xiě)數(shù)據(jù)可能會(huì)發(fā)生混亂。
C++11提供了mutex類(lèi)進(jìn)行加鎖和解鎖。
#include<iostream>#include<thread>#include<mutex>std::mutex mut;class A{public: volatile int temp; A(){ temp=0; } void fun(int num){ int count=10; while(count>0){ mut.lock(); temp++; std::cout<<"thread_"<<num<<"...temp="<<temp<<std::endl; mut.unlock(); count--; } } void thread_run(){ std::thread t1(&A::fun,this,1); std::thread t2(&A::fun,this,2); t1.join(); t2.join(); }};int main(){ A a; a.thread_run();}
然后,我們說(shuō)一下volatile關(guān)鍵字。
volatile和const關(guān)鍵很相似,都是修飾變量的,只是二者功能不一樣。
volatile在多線(xiàn)程當(dāng)中經(jīng)常使用,因?yàn)樵谀骋痪€(xiàn)程多次調(diào)用某一個(gè)變量,編譯器會(huì)進(jìn)行優(yōu)化,將該變量存放在在寄存器當(dāng)中,不會(huì)每次都從內(nèi)存當(dāng)中讀入。果然該變量同時(shí)在其他線(xiàn)程當(dāng)中被修改,這樣就會(huì)發(fā)生臟讀取錯(cuò)誤。
而加上volatile修飾,則會(huì)提醒編譯器,這個(gè)變量可能會(huì)被改變,不能存放到寄存器當(dāng)中,需要每次都從內(nèi)存當(dāng)中讀取。
最后,我們說(shuō)一下join()和detach()的使用技巧。
join及detach上述例子中已經(jīng)用到了join和detach,以下對(duì)兩者進(jìn)行說(shuō)明。 對(duì)于C++程序而言,你的main函數(shù)就是你的主線(xiàn)程,主線(xiàn)程在任何需要的時(shí)候都可以創(chuàng)建新的線(xiàn)程,當(dāng)線(xiàn)程執(zhí)行完畢時(shí),自動(dòng)終止線(xiàn)程;當(dāng)進(jìn)程結(jié)束時(shí),所有線(xiàn)程都必須終止。所謂join就是主線(xiàn)程在調(diào)用join方法的位置等待子線(xiàn)程會(huì)合,會(huì)合之后執(zhí)行下一步操作。所謂detach就是,主線(xiàn)程將不等待子線(xiàn)程的會(huì)合,自己直接結(jié)束生命,子線(xiàn)程未執(zhí)行完也不報(bào)錯(cuò),會(huì)自己在執(zhí)行完畢后會(huì)退出。如果某個(gè)線(xiàn)程,不聲明以上兩者之一,但是在主線(xiàn)程結(jié)束之前又沒(méi)有執(zhí)行完畢,程序執(zhí)行時(shí)會(huì)崩潰。--------------------- 作者:yucicheung 來(lái)源:CSDN 原文:https://blog.csdn.net/yucicheung/article/details/82466302 版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請(qǐng)附上博文鏈接!
聯(lián)系客服