线程自动退出_C++基础 多线程笔记(一)
join & detach
join和detach為最基本的用法,join可以使主線程(main函數(shù))等待子線程(自定義的function_1函數(shù))完成后再退出程序,而detach可以使子線程與主線程毫無關(guān)聯(lián)的獨立運行,當主線程執(zhí)行完畢后直接退出程序,不管子線程是否執(zhí)行完畢。
#include<iostream> #include<thread> using namespace std;// 子線程函數(shù) void function_1() {for(int i=10;i>0;i--) // 循環(huán)10次輸出cout << "=============Hello=============" << endl; }int main() {thread t1(function_1);//線程開始//t1.join();//方式1:結(jié)合(等待其完成)t1.detach();//方式2:分離(使其自行運行)(cout未來得及輸出完畢,主線程已結(jié)束)cout << "~~~~~~~~~~~World~~~~~~~~~~~" << endl;if (t1.joinable()){t1.join();}return 0; }detach方法的執(zhí)行結(jié)果如下,可以看出子線程沒來得及執(zhí)行完畢。
=============Hello============= ~~~~~~~~~~~World~~~~~~~~~~~ =請按任意鍵繼續(xù). . .如果換成join方法,則可以輸出10條Hello語句。
=============Hello============= =============Hello============= =============Hello============= =============Hello============= =============Hello============= =============Hello============= =============Hello============= =============Hello============= =============Hello============= =============Hello============= ~~~~~~~~~~~World~~~~~~~~~~~ 請按任意鍵繼續(xù). . .try-catch異常捕獲機制的使用
join可以使某些比較重要的函數(shù)執(zhí)行完畢后再退出,但當程序出現(xiàn)異常時,程序仍會直接退出,join沒有起到應(yīng)有的作用,這是可以通過try-catch異常捕獲機制,結(jié)合join方法,使某些函數(shù)(子線程)在程序出現(xiàn)異常時也能先執(zhí)行完畢再退出,例子如下,通過OpenCV讀取顯示一張不存在的圖片產(chǎn)生異常。
#include<iostream> #include<thread> #include<opencv2/opencv.hpp>// 子線程函數(shù)(假定該函數(shù)比較重要,無論如何都要執(zhí)行完畢再退出程序) void function_1() {for (int i = 0; i < 100; i++){std::cout << "========Hello=======" << i << std::endl;} }int main() {std::thread t1(function_1);//t1線程開始運行try //【捕獲異常的范圍】{cv::Mat img = cv::imread("1.jpg");//讀取一張不存在的圖片,使下句的圖片顯示出現(xiàn)異常cv::imshow("===", img);//此處將出現(xiàn)異常!?錯誤?//出現(xiàn)異常會導(dǎo)致整個程序直接退出//捕獲異常后,可以進行補救,如使t1子線程執(zhí)行完畢。}catch (...)//捕獲所有異常{std::cout << "catch..............." << std::endl;t1.join();//使子線程執(zhí)行完畢throw;}t1.join();std::cout << "主程序正常退出" << std::endl;return 0; }可以看出運行后產(chǎn)生了一個OpenCV Error,沒能輸出"主程序正常退出" ,但子線程在程序出現(xiàn)異常后依然可以繼續(xù)執(zhí)行完畢。
========Hello=======OpenCV Error: Assertion failed (size.width>0 && size.height>0) in cv::imshow, file D:Tools1opencvopencvsourcesmoduleshighguisrcwindow.cpp, line 325 0 ========Hello=======1catch...............========Hello=======2 ========Hello=======3 ========Hello=======4 ========Hello=======5 此處省略... ========Hello=======98 ========Hello=======99通過類構(gòu)造子線程 & ref方法傳參
C++開發(fā)中更常使用類作為子線程函數(shù)而不是單獨的某個函數(shù)。
注意一點在線程按引用傳遞參數(shù)時的寫法,需要使用std::ref方法。
#include<iostream> #include<thread> #include<string>class Fctor { public:void operator()(std::string& msg)//按【引用】傳遞參數(shù){std::cout << "from t1:" << msg << std::endl; msg = "++++++++Hello+++++++++";//修改傳入的參數(shù)(用于后面的主線程輸出)} };int main() {std::string s = "-----------World-----------";//待傳入的參數(shù)(用于子線程輸出)// 方式1a:這種方式會自動復(fù)制一份參數(shù)傳進去//Fctor fct;//std::thread t1(fct,s);//t1線程開始運行// 方式1b:這種方式會自動復(fù)制一份參數(shù)傳進去//std::thread t1((Fctor()), s);//t1線程開始運行// 方式2a:按引用傳遞Fctor fct;std::thread t1(fct, std::ref(s));//t1線程開始運行// 方式2b:按引用傳遞//std::thread t1((Fctor()), std::ref(s));t1.join();std::cout << "from main:" << s << std::endl;return 0; }運行結(jié)果,方式1a或1b:
from t1:-----------World----------- from main:-----------World----------- 請按任意鍵繼續(xù). . .方式2a或2b:
from t1:-----------World----------- from main:++++++++Hello+++++++++ 請按任意鍵繼續(xù). . .mov方法傳參 & 線程對象移動
除了使用ref方法對子線程進行傳參,還可以使用mov方法傳參,此外mov還可以移動線程對象。
#include<iostream> #include<thread> #include<string>class Fctor { public:void operator()(std::string& msg)//按引用傳遞參數(shù){std::cout << "from t1:" << msg << std::endl;msg = "++++++++++++Hello++++++++++";} };int main() {std::string s = "----------------World---------------";std::cout << "Main_thread_ID:" << std::this_thread::get_id() << std::endl;//主線程IDstd::thread t1((Fctor()), std::move(s));//子線程1(將字符串從主線程移動到子線程)std::cout << "Sub_thread1_ID" << t1.get_id() << std::endl;//線程對象只能被移動,不能被復(fù)制。std::thread t2 = std::move(t1);//子線程2(接管子線程1,此時子線程1為空?!)std::cout << "Sub_thread2_ID" << t2.get_id() << std::endl;//可以看到兩個子線程的ID是相同的!t2.join();//等待子線程2結(jié)束//檢測硬件并發(fā)特性(此句只是用來顯示計算機支持的并發(fā)線程數(shù)量)std::cout << std::thread::hardware_concurrency() << std::endl;return 0; }運行結(jié)果如下,可以看出傳參無誤,并且兩個子線程的ID相同,說明子線程對象移動成功。
Main_thread_ID:36576 from t1:Sub_thread1_ID37472----------------World---------------Sub_thread2_ID37472 8 請按任意鍵繼續(xù). . .mutex & lock_guard
mutex即互斥量,可理解為一把鎖,訪問某些資源時先加鎖,訪問后解鎖。 另一進程訪問同一資源時,首先嘗試加鎖,如果鎖處于未釋放狀態(tài)則無法加鎖,需等待其它線程對鎖的釋放。
#include<iostream> #include<thread> #include<string> #include<mutex>std::mutex mu;//【互斥對象】=》一把鎖通過函數(shù)調(diào)用cout,并為cout加鎖,防止同時訪問cout void share_print(std::string msg, int id) {mu.lock();std::cout << msg << id << std::endl;mu.unlock(); }//子線程函數(shù) void function_1() {for(int i = 0; i < 100; i++)share_print("==========from t1:" ,i ); }int main()//主線程 {std::thread t1(function_1);//t1線程開始運行for (int i = 0; i < 100; i++){share_print("+++++++++++++++++from main:", i);}t1.join();//等待子線程結(jié)束return 0; }運行結(jié)果類似如下:
==========from t1:0 +++++++++++++++++from main:0 ==========from t1:1 +++++++++++++++++from main:1 ==========from t1:2 ==========from t1:3 ==========from t1:4 ==========from t1:5 省略...如果未使用加鎖機制,兩線程會互相爭搶cout的使用權(quán),從而導(dǎo)致輸出混亂,注釋掉mu.lock()與mu.unlock()后的輸出結(jié)果如下:
==========from t1:0+++++++++++++++++from main:0==========from t1:1+++++++++++++++++from main:1==========from t1:2+++++++++++++++++from main:2==========from t1:3 +++++++++++++++++from main:3==========from t1:4==========from t1:5+++++++++++++++++from main:4 省略...由于lock()與unlock()必須成對出現(xiàn),為方便管理,出現(xiàn)了lock_guard,它可以對mutex進行管理,自動實現(xiàn)lock()與unlock(),原理是在其構(gòu)造與析構(gòu)中自動調(diào)用。另外,還可有附加參數(shù)。
修改上面的share_print為如下,可實現(xiàn)同樣的效果。
void share_print(std::string msg, int id) {std::lock_guard<std::mutex> guard(mu);std::cout << msg << id << std::endl; }下面的代碼是將share_print封裝到一個類中,并添加將輸出信息同時保存到文件中的功能:
#include<iostream> #include<thread> #include<string> #include<mutex> #include<fstream>class LofFile { public:LofFile(){ f.open("log.txt"); }~LofFile(){ f.close(); }void shared_print(std::string id, int value){std::lock_guard<std::mutex> locker(m_mutex);f << "from " << id << ":" << value << std::endl;//寫入文件std::cout << "from " << id << ":" << value << std::endl;//輸出}private://受保護的成員std::mutex m_mutex;//鎖std::ofstream f;//此時f完全在鎖的保護下 };void function_1(LofFile& log) {for (int i = 0; i > -100; i--)log.shared_print("t1", i); }int main()//主線程 {LofFile log;std::thread t1(function_1,std::ref(log));//t1線程開始運行for (int i = 0; i < 100; i++){log.shared_print("main", i);}t1.join();return 0; }死鎖 & adopt_lock
當某個資源被兩把以上的鎖嵌套加鎖,且鎖的順序不一致時,可能發(fā)生死鎖。
原因在于多個線程可能各自加了1把鎖后,同時在等待對方釋放剩余的鎖。
最簡單的解決方法是:只要鎖的順序一致,就不會死鎖。
#include<iostream> #include<thread> #include<string> #include<mutex> #include<fstream>class LogFile {std::mutex m_mutex;//鎖1std::mutex m_mutex2;//鎖2std::ofstream f; public:LogFile()//構(gòu)造函數(shù),初始化時新建一個txt文件{f.open("log.txt");}void shared_print(std::string id, int value){std::lock_guard<std::mutex> locker(m_mutex);//鎖住m_mutex成員std::lock_guard<std::mutex> locker2(m_mutex2);std::cout << id << ":" << value << std::endl;}void shared_print2(std::string id, int value){std::lock_guard<std::mutex> locker2(m_mutex2);//【出現(xiàn)死所,交換和下一行的位置即可】std::lock_guard<std::mutex> locker(m_mutex);//std::lock_guard<std::mutex> locker2(m_mutex2);std::cout << id << ":" << value << std::endl;} };void function_1(LogFile& log) {for (int i = 0; i > -1000; i--)log.shared_print(std::string("from t1:"), i); }int main()//主線程 {LogFile log;std::thread t1(function_1, std::ref(log));//t1線程開始運行for (int i = 0; i < 1000; i++){log.shared_print2(std::string("from main:"), i);}t1.join();return 0; }某次運行結(jié)果如下,程序運行到某時刻卡住了:
from main::0 from main::1 省略... from main::154 from main::155 from main::156 from main::157 from t1::0當程序比較復(fù)雜時,手動方法管理加鎖順序可能相當麻煩,這是就出現(xiàn)了adopt_lock參數(shù)來解決。
lock+lock_guard的adopt_lock參數(shù)自動避免死鎖問題。
lock()可同時管理多個鎖,順序無影響,同時鎖住多個鎖,若不可,先釋放,然后繼續(xù)嘗試。 lock_guard()的adopt_lock參數(shù)即拋棄lock操作,因為前面(必須)已加鎖,只使用其自動unlock功能。
#include<iostream> #include<thread> #include<string> #include<mutex> #include<fstream>class LogFile {std::mutex m_mutex;//鎖1std::mutex m_mutex2;//鎖2std::ofstream f; public:LogFile(){f.open("log.txt");}void shared_print(std::string id, int value){std::lock(m_mutex, m_mutex2);//lock()同時管理多個鎖std::lock_guard<std::mutex> locker(m_mutex,std::adopt_lock);//adopt_lock即拋棄lock操作,因為上句已加鎖std::lock_guard<std::mutex> locker2(m_mutex2, std::adopt_lock);//在析構(gòu)時自動unlock()std::cout << id << ":" << value << std::endl;}void shared_print2(std::string id, int value){std::lock(m_mutex, m_mutex2);std::lock_guard<std::mutex> locker2(m_mutex2, std::adopt_lock);std::lock_guard<std::mutex> locker(m_mutex, std::adopt_lock);std::cout << id << ":" << value << std::endl;} };void function_1(LogFile& log) {for (int i = 0; i > -1000; i--)log.shared_print(std::string("from t1:"), i); }int main()//主線程 {LogFile log;std::thread t1(function_1, std::ref(log));//t1線程開始運行for (int i = 0; i < 1000; i++){log.shared_print2(std::string("from main:"), i);}t1.join();return 0; }運行結(jié)果如下,不會出現(xiàn)死鎖:
from t1::0 from main::0 from t1::-1 from main::1 省略... from t1::-997 from main::994 from t1::-998 from main::995 from t1::-999 from main::996 from main::997 from main::998 from main::999 請按任意鍵繼續(xù). . .總結(jié)
以上是生活随笔為你收集整理的线程自动退出_C++基础 多线程笔记(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python微信语音转发方法_微信怎么转
- 下一篇: android 微信6.1版本,微信6.