小編給大家分享一下C++11多線程編程的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
為湯旺等地區(qū)用戶提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及湯旺網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為網(wǎng)站建設(shè)、網(wǎng)站制作、湯旺網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!
1.在C++11中創(chuàng)建新線程
在每個(gè)c++應(yīng)用程序中,都有一個(gè)默認(rèn)的主線程,即main函數(shù),在c++11中,我們可以通過(guò)創(chuàng)建std::thread類的對(duì)象來(lái)創(chuàng)建其他線程,每個(gè)std :: thread對(duì)象都可以與一個(gè)線程相關(guān)聯(lián),只需包含頭文件< thread>??梢允褂胹td :: thread對(duì)象附加一個(gè)回調(diào),當(dāng)這個(gè)新線程啟動(dòng)時(shí),它將被執(zhí)行。 這些回調(diào)可以為函數(shù)指針、函數(shù)對(duì)象、Lambda函數(shù)。
線程對(duì)象可通過(guò)std::thread thObj(< CALLBACK>)來(lái)創(chuàng)建,新線程將在創(chuàng)建新對(duì)象后立即開(kāi)始,并且將與已啟動(dòng)的線程并行執(zhí)行傳遞的回調(diào)。此外,任何線程可以通過(guò)在該線程的對(duì)象上調(diào)用join()函數(shù)來(lái)等待另一個(gè)線程退出。
使用函數(shù)指針創(chuàng)建線程:
//main.cpp #include <iostream> #include <thread> void thread_function() { for (int i = 0; i < 5; i++) std::cout << "thread function excuting" << std::endl; }int main() { std::thread threadObj(thread_function); for (int i = 0; i < 5; 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 < 100; i++) std::cout << "Display Thread Excecuting" << std::endl; } }; int main() { std::thread threadObj((DisplayThread())); for (int i = 0; i < 100; 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; }
CmakeLists.txt
cmake_minimum_required(VERSION 3.10) project(Thread_test)set(CMAKE_CXX_STANDARD 11) find_package(Threads REQUIRED) add_executable(Thread_test main.cpp) target_link_libraries(Thread_test ${CMAKE_THREAD_LIBS_INIT})
每個(gè)std::thread對(duì)象都有一個(gè)相關(guān)聯(lián)的id,std::thread::get_id() —-成員函數(shù)中給出對(duì)應(yīng)線程對(duì)象的id;
std::this_thread::get_id()—-給出當(dāng)前線程的id,如果std::thread對(duì)象沒(méi)有關(guān)聯(lián)的線程,get_id()將返回默認(rèn)構(gòu)造的std::thread::id對(duì)象:“not any thread”,std::thread::id也可以表示id。
線程一旦啟動(dòng),另一個(gè)線程可以通過(guò)調(diào)用std::thread對(duì)象上調(diào)用join()函數(shù)等待這個(gè)線程執(zhí)行完畢:
std::thread threadObj(funcPtr); threadObj.join();
例如,主線程啟動(dòng)10個(gè)線程,啟動(dòng)完畢后,main函數(shù)等待他們執(zhí)行完畢,join完所有線程后,main函數(shù)繼續(xù)執(zhí)行:
#include <iostream> #include <thread> #include <algorithm> class WorkerThread { public:void operator()(){ std::cout<<"Worker Thread "<<std::this_thread::get_id()<<"is Excecuting"<<std::endl; } }; int main(){ std::vector<std::thread> threadList; for(int i = 0; i < 10; i++){ threadList.push_back(std::thread(WorkerThread())); } // Now wait for all the worker thread to finish i.e. // Call join() function on each of the std::thread object std::cout<<"Wait for all the worker thread to finish"<<std::endl; std::for_each(threadList.begin(), threadList.end(), std::mem_fn(&std::thread::join)); std::cout<<"Exiting from Main Thread"<<std::endl; return 0; }
detach可以將線程與線程對(duì)象分離,讓線程作為后臺(tái)線程執(zhí)行,當(dāng)前線程也不會(huì)阻塞了.但是detach之后就無(wú)法在和線程發(fā)生聯(lián)系了.如果線程執(zhí)行函數(shù)使用了臨時(shí)變量可能會(huì)出現(xiàn)問(wèn)題,線程調(diào)用了detach在后臺(tái)運(yùn)行,臨時(shí)變量可能已經(jīng)銷毀,那么線程會(huì)訪問(wèn)已經(jīng)被銷毀的變量,需要在std::thread對(duì)象中調(diào)用std::detach()函數(shù):
std::thread threadObj(funcPtr) threadObj.detach();
調(diào)用detach()后,std::thread對(duì)象不再與實(shí)際執(zhí)行線程相關(guān)聯(lián),在線程句柄上調(diào)用detach() 和 join()要小心.
要將參數(shù)傳遞給線程的可關(guān)聯(lián)對(duì)象或函數(shù),只需將參數(shù)傳遞給std::thread構(gòu)造函數(shù),默認(rèn)情況下,所有的參數(shù)都將復(fù)制到新線程的內(nèi)部存儲(chǔ)中。
給線程傳遞參數(shù):
#include <iostream> #include <string> #include <thread> void threadCallback(int x, std::string str) { std::cout << "Passed Number = " << x << std::endl; std::cout << "Passed String = " << str << std::endl; }int main() { int x = 10; std::string str = "Sample String"; std::thread threadObj(threadCallback, x, str); threadObj.join(); return 0; }
給線程傳遞引用:
#include <iostream> #include <thread> void threadCallback(int const& x) { int& y = const_cast<int&>(x); y++; std::cout << "Inside Thread x = " << x << std::endl; }int main() { int x = 9; std::cout << "In Main Thread : Before Thread Start x = " << x << std::endl; std::thread threadObj(threadCallback, x); threadObj.join(); std::cout << "In Main Thread : After Thread Joins x = " << x << std::endl; return 0; }
輸出結(jié)果為:
In Main Thread : Before Thread Start x = 9
Inside Thread x = 10
In Main Thread : After Thread Joins x = 9
Process finished with exit code 0
即使threadCallback接受參數(shù)作為引用,但是并沒(méi)有改變main中x的值,在線程引用外它是不可見(jiàn)的。這是因?yàn)榫€程函數(shù)threadCallback中的x是引用復(fù)制在新線程的堆棧中的臨時(shí)值,使用std::ref可進(jìn)行修改:
#include <iostream> #include <thread> void threadCallback(int const& x) { int& y = const_cast<int&>(x); y++; std::cout << "Inside Thread x = " << x << std::endl; }int main() { int x = 9; std::cout << "In Main Thread : Before Thread Start x = " << x << std::endl; std::thread threadObj(threadCallback, std::ref(x)); threadObj.join(); std::cout << "In Main Thread : After Thread Joins x = " << x << std::endl; return 0; }
輸出結(jié)果為:
In Main Thread : Before Thread Start x = 9
Inside Thread x = 10
In Main Thread : After Thread Joins x = 10
Process finished with exit code 0
指定一個(gè)類的成員函數(shù)的指針作為線程函數(shù),將指針傳遞給成員函數(shù)作為回調(diào)函數(shù),并將指針指向?qū)ο笞鳛榈诙€(gè)參數(shù):
#include <iostream> #include <thread> class DummyClass { public: DummyClass() { } DummyClass(const DummyClass& obj) { } void sampleMemberfunction(int x) { std::cout << "Inside sampleMemberfunction " << x << std::endl; } }; int main() { DummyClass dummyObj; int x = 10; std::thread threadObj(&DummyClass::sampleMemberfunction, &dummyObj, x); threadObj.join(); return 0; }
在多線程間的數(shù)據(jù)共享很簡(jiǎn)單,但是在程序中的這種數(shù)據(jù)共享可能會(huì)引起問(wèn)題,其中一種便是競(jìng)爭(zhēng)條件。當(dāng)兩個(gè)或多個(gè)線程并行執(zhí)行一組操作,訪問(wèn)相同的內(nèi)存位置,此時(shí),它們中的一個(gè)或多個(gè)線程會(huì)修改內(nèi)存位置中的數(shù)據(jù),這可能會(huì)導(dǎo)致一些意外的結(jié)果,這就是競(jìng)爭(zhēng)條件。競(jìng)爭(zhēng)條件通常較難發(fā)現(xiàn)并重現(xiàn),因?yàn)樗鼈儾⒉豢偸浅霈F(xiàn),只有當(dāng)兩個(gè)或多個(gè)線程執(zhí)行操作的相對(duì)順序?qū)е乱馔饨Y(jié)果時(shí),它們才會(huì)發(fā)生。
例如創(chuàng)建5個(gè)線程,這些線程共享類Wallet的一個(gè)對(duì)象,使用addMoney()成員函數(shù)并行添加100元。所以,如果最初錢(qián)包中的錢(qián)是0,那么在所有線程的競(jìng)爭(zhēng)執(zhí)行完畢后,錢(qián)包中的錢(qián)應(yīng)該是500,但是,由于所有線程同時(shí)修改共享數(shù)據(jù),在某些情況下,錢(qián)包中的錢(qián)可能遠(yuǎn)小于500。
測(cè)試如下:
#include <iostream> #include <thread> #include <algorithm> class Wallet { int mMoney; public: Wallet() : mMoney(0) { } int getMoney() { return mMoney; } void addMoney(int money) { for (int i = 0; i < money; i++) { mMoney++; } } };int testMultithreadWallet() { Wallet walletObject; std::vector<std::thread> threads; for (int i = 0; i < 5; i++) { threads.push_back(std::thread(&Wallet::addMoney, &walletObject, 100)); } for (int i = 0; i < 5; i++) { threads.at(i).join(); } return walletObject.getMoney(); }int main() { int val = 0; for (int k = 0; k < 100; k++) { if ((val=testMultithreadWallet()) != 500) { std::cout << "Error at count = " << k << " Money in Wallet = " << val << std::endl; } } return 0; }
每個(gè)線程并行地增加相同的成員變量“mMoney”,看似是一條線,但是這個(gè)“nMoney++”實(shí)際上被轉(zhuǎn)換為3條機(jī)器命令:
·在Register中加載”mMoney”變量
·增加register的值
·用register的值更新“mMoney”變量
在這種情況下,一個(gè)增量將被忽略,因?yàn)椴皇窃黾觤Money變量,而是增加不同的寄存器,“mMoney”變量的值被覆蓋。
為了處理多線程環(huán)境中的競(jìng)爭(zhēng)條件,我們需要mutex互斥鎖,在修改或讀取共享數(shù)據(jù)前,需要對(duì)數(shù)據(jù)加鎖,修改完成后,對(duì)數(shù)據(jù)進(jìn)行解鎖。在c++11的線程庫(kù)中,mutexes在< mutexe >頭文件中,表示互斥體的類是std::mutex。
就上面的問(wèn)題進(jìn)行處理,Wallet類提供了在Wallet中增加money的方法,并且在不同的線程中使用相同的Wallet對(duì)象,所以我們需要對(duì)Wallet的addMoney()方法加鎖。在增加Wallet中的money前加鎖,并且在離開(kāi)該函數(shù)前解鎖,看代碼:Wallet類內(nèi)部維護(hù)money,并提供函數(shù)addMoney(),這個(gè)成員函數(shù)首先獲取一個(gè)鎖,然后給wallet對(duì)象的money增加指定的數(shù)額,最后釋放鎖。
#include <iostream> #include <thread> #include <vector> #include <mutex> class Wallet { int mMoney; std::mutex mutex;public: Wallet() : mMoney(0) { } int getMoney() { return mMoney;} void addMoney(int money) { mutex.lock(); for (int i = 0; i < money; i++) { mMoney++; } mutex.unlock(); } };int testMultithreadWallet() { Wallet walletObject; std::vector<std::thread> threads; for (int i = 0; i < 5; ++i) { threads.push_back(std::thread(&Wallet::addMoney, &walletObject, 1000)); } for (int i = 0; i < threads.size(); i++) { threads.at(i).join(); } return walletObject.getMoney(); }int main() { int val = 0; for (int k = 0; k < 1000; k++) { if ((val = testMultithreadWallet()) != 5000) { std::cout << "Error at count= " << k << " money in wallet" << val << std::endl; } } return 0; }
這種情況保證了錢(qián)包里的錢(qián)不會(huì)出現(xiàn)少于5000的情況,因?yàn)閍ddMoney()中的互斥鎖確保了只有在一個(gè)線程修改完成money后,另一個(gè)線程才能對(duì)其進(jìn)行修改,但是,如果我們忘記在函數(shù)結(jié)束后對(duì)鎖進(jìn)行釋放會(huì)怎么樣?這種情況下,一個(gè)線程將退出而不釋放鎖,其他線程將保持等待,為了避免這種情況,我們應(yīng)當(dāng)使用std::lock_guard,這是一個(gè)template class,它為mutex實(shí)現(xiàn)RALL,它將mutex包裹在其對(duì)象內(nèi),并將附加的mutex鎖定在其構(gòu)造函數(shù)中,當(dāng)其析構(gòu)函數(shù)被調(diào)用時(shí),它將釋放互斥體。
class Wallet { int mMoney; std::mutex mutex; public: Wallet() : mMoney(0) { } int getMoney() { return mMoney;} void addMoney(int money) { std::lock_guard<std::mutex> lockGuard(mutex); for (int i = 0; i < mMoney; ++i) { //如果在此處發(fā)生異常,lockGuadr的析構(gòu)函數(shù)將會(huì)因?yàn)槎褩U归_(kāi)而被調(diào)用 mMoney++; //一旦函數(shù)退出,那么lockGuard對(duì)象的析構(gòu)函數(shù)將被調(diào)用,在析構(gòu)函數(shù)中mutex會(huì)被釋放 } } };
條件變量是一種用于在2個(gè)線程之間進(jìn)行信令的事件,一個(gè)線程可以等待它得到信號(hào),其他的線程可以給它發(fā)信號(hào)。在c++11中,條件變量需要頭文件< condition_variable>,同時(shí),條件變量還需要一個(gè)mutex鎖。
條件變量是如何運(yùn)行的:
·線程1調(diào)用等待條件變量,內(nèi)部獲取mutex互斥鎖并檢查是否滿足條件;
·如果沒(méi)有,則釋放鎖,并等待條件變量得到發(fā)出的信號(hào)(線程被阻塞),條件變量的wait()函數(shù)以原子方式提供這兩個(gè)操作;
·另一個(gè)線程,如線程2,當(dāng)滿足條件時(shí),向條件變量發(fā)信號(hào);
·一旦線程1正等待其恢復(fù)的條件變量發(fā)出信號(hào),線程1便獲取互斥鎖,并檢查與條件變量相關(guān)關(guān)聯(lián)的條件是否滿足,或者是否是一個(gè)上級(jí)調(diào)用,如果多個(gè)線程正在等待,那么notify_one將只解鎖一個(gè)線程;
·如果是一個(gè)上級(jí)調(diào)用,那么它再次調(diào)用wait()函數(shù)。
條件變量的主要成員函數(shù):
Wait()
它使得當(dāng)前線程阻塞,直到條件變量得到信號(hào)或發(fā)生虛假喚醒;
它原子性地釋放附加的mutex,阻塞當(dāng)前線程,并將其添加到等待當(dāng)前條件變量對(duì)象的線程列表中,當(dāng)某線程在同樣的條件變量上調(diào)用notify_one() 或者 notify_all(),線程將被解除阻塞;
這種行為也可能是虛假的,因此,解除阻塞后,需要再次檢查條件;
一個(gè)回調(diào)函數(shù)會(huì)傳給該函數(shù),調(diào)用它來(lái)檢查其是否是虛假調(diào)用,還是確實(shí)滿足了真實(shí)條件;
當(dāng)線程解除阻塞后,wait()函數(shù)獲取mutex鎖,并檢查條件是否滿足,如果條件不滿足,則再次原子性地釋放附加的mutex,阻塞當(dāng)前線程,并將其添加到等待當(dāng)前條件變量對(duì)象的線程列表中。
notify_one()
如果所有線程都在等待相同的條件變量對(duì)象,那么notify_one會(huì)取消阻塞其中一個(gè)等待線程。
notify_all()
如果所有線程都在等待相同的條件變量對(duì)象,那么notify_all會(huì)取消阻塞所有的等待線程。
#include <iostream> #include <thread> #include <functional> #include <mutex> #include <condition_variable> using namespace std::placeholders; class Application { std::mutex m_mutex; std::condition_variable m_condVar; bool m_bDataLoaded;public: Application() { m_bDataLoaded = false; } void loadData() { //使該線程sleep 1秒 std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::cout << "Loading Data from XML" << std::endl; //鎖定數(shù)據(jù) std::lock_guard<std::mutex> guard(m_mutex); //flag設(shè)為true,表明數(shù)據(jù)已加載 m_bDataLoaded = true; //通知條件變量 m_condVar.notify_one(); } bool isDataLoaded() { return m_bDataLoaded; } void mainTask() { std::cout << "Do some handshaking" << std::endl; //獲取鎖 std::unique_lock<std::mutex> mlock(m_mutex); //開(kāi)始等待條件變量得到信號(hào) //wait()將在內(nèi)部釋放鎖,并使線程阻塞 //一旦條件變量發(fā)出信號(hào),則恢復(fù)線程并再次獲取鎖 //然后檢測(cè)條件是否滿足,如果條件滿足,則繼續(xù),否則再次進(jìn)入wait m_condVar.wait(mlock, std::bind(&Application::isDataLoaded, this)); std::cout << "Do Processing On loaded Data" << std::endl; } };int main() { Application app; std::thread thread_1(&Application::mainTask, &app); std::thread thread_2(&Application::loadData, &app); thread_2.join(); thread_1.join(); return 0; }
以上是“C++11多線程編程的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
標(biāo)題名稱:C++11多線程編程的示例分析
當(dāng)前網(wǎng)址:http://jinyejixie.com/article28/jjiocp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、、網(wǎng)站維護(hù)、移動(dòng)網(wǎng)站建設(shè)、商城網(wǎng)站、外貿(mào)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)