線程自動退出_C++基礎 多線程筆記(一)

e08cf3f8ba5a84cf8806545fe0d5dcf6.png

join & detach

join和detach為最基本的用法,join可以使主線程(main函數)等待子線程(自定義的function_1函數)完成后再退出程序,而detach可以使子線程與主線程毫無關聯的獨立運行,當主線程執行完畢后直接退出程序,不管子線程是否執行完畢。

#include<iostream>
#include<thread>
using namespace std;// 子線程函數
void function_1()
{for(int i=10;i>0;i--) // 循環10次輸出cout << "=============Hello=============" << endl;
}int main()
{thread t1(function_1);//線程開始//t1.join();//方式1:結合(等待其完成)t1.detach();//方式2:分離(使其自行運行)(cout未來得及輸出完畢,主線程已結束)cout << "~~~~~~~~~~~World~~~~~~~~~~~" << endl;if (t1.joinable()){t1.join();}return 0;
}

detach方法的執行結果如下,可以看出子線程沒來得及執行完畢。

=============Hello=============
~~~~~~~~~~~World~~~~~~~~~~~
=請按任意鍵繼續. . .

如果換成join方法,則可以輸出10條Hello語句。

=============Hello=============
=============Hello=============
=============Hello=============
=============Hello=============
=============Hello=============
=============Hello=============
=============Hello=============
=============Hello=============
=============Hello=============
=============Hello=============
~~~~~~~~~~~World~~~~~~~~~~~
請按任意鍵繼續. . .

try-catch異常捕獲機制的使用

join可以使某些比較重要的函數執行完畢后再退出,但當程序出現異常時,程序仍會直接退出,join沒有起到應有的作用,這是可以通過try-catch異常捕獲機制,結合join方法,使某些函數(子線程)在程序出現異常時也能先執行完畢再退出,例子如下,通過OpenCV讀取顯示一張不存在的圖片產生異常。

#include<iostream>
#include<thread>
#include<opencv2/opencv.hpp>// 子線程函數(假定該函數比較重要,無論如何都要執行完畢再退出程序)
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");//讀取一張不存在的圖片,使下句的圖片顯示出現異常cv::imshow("===", img);//此處將出現異常!?錯誤?//出現異常會導致整個程序直接退出//捕獲異常后,可以進行補救,如使t1子線程執行完畢。}catch (...)//捕獲所有異常{std::cout << "catch..............." << std::endl;t1.join();//使子線程執行完畢throw;}t1.join();std::cout << "主程序正常退出" << std::endl;return 0;
}

可以看出運行后產生了一個OpenCV Error,沒能輸出"主程序正常退出" ,但子線程在程序出現異常后依然可以繼續執行完畢。

========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

通過類構造子線程 & ref方法傳參

C++開發中更常使用類作為子線程函數而不是單獨的某個函數。

注意一點在線程按引用傳遞參數時的寫法,需要使用std::ref方法。

#include<iostream>
#include<thread>
#include<string>class Fctor
{
public:void operator()(std::string& msg)//按【引用】傳遞參數{std::cout << "from t1:" << msg << std::endl; msg = "++++++++Hello+++++++++";//修改傳入的參數(用于后面的主線程輸出)}
};int main()
{std::string s = "-----------World-----------";//待傳入的參數(用于子線程輸出)// 方式1a:這種方式會自動復制一份參數傳進去//Fctor fct;//std::thread t1(fct,s);//t1線程開始運行// 方式1b:這種方式會自動復制一份參數傳進去//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;
}

運行結果,方式1a或1b:

from t1:-----------World-----------
from main:-----------World-----------
請按任意鍵繼續. . .

方式2a或2b:

from t1:-----------World-----------
from main:++++++++Hello+++++++++
請按任意鍵繼續. . .

mov方法傳參 & 線程對象移動

除了使用ref方法對子線程進行傳參,還可以使用mov方法傳參,此外mov還可以移動線程對象。

#include<iostream>
#include<thread>
#include<string>class Fctor
{
public:void operator()(std::string& msg)//按引用傳遞參數{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;//線程對象只能被移動,不能被復制。std::thread t2 = std::move(t1);//子線程2(接管子線程1,此時子線程1為空?!)std::cout << "Sub_thread2_ID" << t2.get_id() << std::endl;//可以看到兩個子線程的ID是相同的!t2.join();//等待子線程2結束//檢測硬件并發特性(此句只是用來顯示計算機支持的并發線程數量)std::cout << std::thread::hardware_concurrency() << std::endl;return 0;
}

運行結果如下,可以看出傳參無誤,并且兩個子線程的ID相同,說明子線程對象移動成功。

Main_thread_ID:36576
from t1:Sub_thread1_ID37472----------------World---------------Sub_thread2_ID37472
8
請按任意鍵繼續. . .

mutex & lock_guard

mutex即互斥量,可理解為一把鎖,訪問某些資源時先加鎖,訪問后解鎖。 另一進程訪問同一資源時,首先嘗試加鎖,如果鎖處于未釋放狀態則無法加鎖,需等待其它線程對鎖的釋放。

#include<iostream>
#include<thread>
#include<string>
#include<mutex>std::mutex mu;//【互斥對象】=》一把鎖通過函數調用cout,并為cout加鎖,防止同時訪問cout
void share_print(std::string msg, int id)
{mu.lock();std::cout << msg << id << std::endl;mu.unlock();
}//子線程函數
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();//等待子線程結束return 0;
}

運行結果類似如下:

==========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的使用權,從而導致輸出混亂,注釋掉mu.lock()與mu.unlock()后的輸出結果如下:

==========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()必須成對出現,為方便管理,出現了lock_guard,它可以對mutex進行管理,自動實現lock()與unlock(),原理是在其構造與析構中自動調用。另外,還可有附加參數。

修改上面的share_print為如下,可實現同樣的效果。

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

當某個資源被兩把以上的鎖嵌套加鎖,且鎖的順序不一致時,可能發生死鎖。

原因在于多個線程可能各自加了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()//構造函數,初始化時新建一個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);//【出現死所,交換和下一行的位置即可】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;
}

某次運行結果如下,程序運行到某時刻卡住了:

from main::0
from main::1
省略...
from main::154
from main::155
from main::156
from main::157
from t1::0

當程序比較復雜時,手動方法管理加鎖順序可能相當麻煩,這是就出現了adopt_lock參數來解決。

lock+lock_guard的adopt_lock參數自動避免死鎖問題。

lock()可同時管理多個鎖,順序無影響,同時鎖住多個鎖,若不可,先釋放,然后繼續嘗試。 lock_guard()的adopt_lock參數即拋棄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);//在析構時自動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;
}

運行結果如下,不會出現死鎖:

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
請按任意鍵繼續. . .

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/457844.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/457844.shtml
英文地址,請注明出處:http://en.pswp.cn/news/457844.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

WEB在線預覽PDF

這是我在博客園發表的第一篇文章。以后會陸續把在線預覽其他格式文檔的解決方案發表出來。 解決思路&#xff1a;把pdf轉換成html顯示。 在線預覽pdf我暫時了解3種解決方案&#xff0c;歡迎大家補充。 方案一&#xff1a; 利用pdf2html軟件將PDF轉換成HTML。 用法: PDF2HTML [選…

[算法]判斷一個數是不是2的N次方

如果一個數是2^n&#xff0c;說明這個二進制里面只有一個1。除了1. a (10000)b a-1 (01111)b a&(a-1) 0。 如果一個數不是2^n&#xff0c; 說明它的二進制里含有多一個1。 a (1xxx100)b a-1(1xxx011)b 那么 a&(a-1)就是 (1xxx000)b&#xff0c; 而不會為0。 所以可…

VMware Ubuntu 全屏問題解決

在終端中輸入&#xff1a; sudo apt install open-vm* 回車 自動解決

數組拼接時中間怎么加入空格_【題解二維數組】1123:圖像相似度

1123&#xff1a;圖像相似度時間限制: 1000 ms 內存限制: 65536 KB【題目描述】給出兩幅相同大小的黑白圖像(用0-1矩陣)表示&#xff0c;求它們的相似度。說明&#xff1a;若兩幅圖像在相同位置上的像素點顏色相同&#xff0c;則稱它們在該位置具有相同的像素點。兩幅圖像的…

(舊)子數涵數·C語言——條件語句

首先&#xff0c;我們講一下理論知識&#xff0c;在編程中有三種結構&#xff0c;分別是順序結構、條件結構、循環結構&#xff0c;如果用流程圖來表示的話就是&#xff1a; 那么在C語言中&#xff0c;如何靈活運用這三種結構呢&#xff1f;這就需要用到控制語句了。 而條件語句…

apache.commons.lang.StringUtils 使用心得

apache.commons.lang.StringUtils 使用心得 轉載于:https://www.cnblogs.com/qinglizlp/p/5549687.html

python哪個版本支持xp_windows支持哪個版本的python

Windows操作系統支持Python的Python2版本和Python3版本&#xff0c;下載安裝時要根據windows的操作系統來選擇對應的Python安裝包&#xff0c;否則將不能安裝成功。 Python是跨平臺的&#xff0c;免費開源的一門計算機編程語言。是一種面向對象的動態類型語言&#xff0c;最初被…

Ubuntu 鍵盤錯位解決 更改鍵盤布局

原因是鍵盤布局不能適應鍵盤 解絕方法&#xff1a;更改鍵盤布局 一般改為標準104鍵盤就行 在終端輸入 sudo dpkg-reconfigure keyboard-configuration 選擇 標準104鍵盤 然后一直回車就行

【No.1 Ionic】基礎環境配置

Node 安裝git clone https://github.com/nodejs/node cd node ./configure make sudo make install node -v npm -vnpm設置淘寶鏡像npm config set registry https://registry.npm.taobao.org npm config set disturl https://npm.taobao.org/distIOS Simulatorsudo npm instal…

識別操作系統

使用p0f進行操作系統探測 p0f是一款被動探測工具&#xff0c;通過分析網絡數據包來判斷操作系統類型。目前最新版本為3.06b。同時p0f在網絡分析方面功能強大&#xff0c;可以用它來分析NAT、負載均衡、應用代理等。 p0f的命令參數很簡單&#xff0c;基本說明如下&#xff1a; l…

常用RGB顏色表

轉載于:https://www.cnblogs.com/Itwonderful/p/5550800.html

python中seek函數的用法_在Python中操作文件之seek()方法的使用教程

seek()方法在偏移設定該文件的當前位置。參數是可選的&#xff0c;默認為0&#xff0c;這意味著絕對的文件定位&#xff0c;它的值如果是1&#xff0c;這意味著尋求相對于當前位置&#xff0c;2表示相對于文件的末尾。 沒有返回值。需要注意的是&#xff0c;如果該文件被打開或…

WPF中Grid實現網格,表格樣式通用類(轉)

/// <summary> /// 給Grid添加邊框線 /// </summary> /// <param name"grid"></param> public static void InsertFrameForGrid(Grid grid) { var rowcon grid.RowDefinitions.Count; var clcon grid.ColumnDefinitions.Count; for (var i…

VS2017 安裝 QT5.9

VS2017專業版使用最新版Qt5.9.2教程&#xff08;最新教材&#xff09; 目錄 VS2017專業版使用最新版Qt5.9.2教程&#xff08;最新教材&#xff09; 運行環境&#xff1a; 1.安裝Qt5.9.2 2.安裝Qt5.9與VS2017之間的插件: 3.配置Qt VS Tool的環境. 4.設置創建的Qt的項目的屬…

異步與并行~ReaderWriterLockSlim實現的共享鎖和互斥鎖

返回目錄 在System.Threading.Tasks命名空間下&#xff0c;使用ReaderWriterLockSlim對象來實現多線程并發時的鎖管理&#xff0c;它比lock來說&#xff0c;性能更好&#xff0c;也并合理&#xff0c;我們都知道lock可以對代碼塊進行鎖定&#xff0c;當多線程共同訪問代碼時&am…

linux ssh yum升級_Linux 運維必備的 13 款實用工具,拿好了

作者丨Erstickthttp://blog.51cto.com/13740508/2114819本文介紹幾款 Linux 運維比較實用的工具&#xff0c;希望對 Linux 運維人員有所幫助。1. 查看進程占用帶寬情況 - NethogsNethogs 是一個終端下的網絡流量監控工具可以直觀的顯示每個進程占用的帶寬。下載&#xff1a;htt…

iOS應用如何支持IPV6

本文轉自 http://www.code4app.com/forum.php?modviewthread&tid8427&highlightipv6 果然是蘋果打個哈欠&#xff0c;iOS行業內就得起一次風暴呀。自從5月初Apple明文規定所有開發者在6月1號以后提交新版本需要支持IPV6-Only的網絡&#xff0c;大家便開始熱火朝天的研…

SQL Server -- SQLserver 存儲過程執行錯誤記錄到表

SQLserver 存儲過程執行錯誤記錄到表 From: http://blog.csdn.net/leshami/article/details/51333650 對于在執行存儲過程中碰到的一些錯誤&#xff0c;如果未及時捕獲或者說傳遞給前端應用程序來&#xff0c;在這樣的情形下&#xff0c;故障的排查顯得尤為困難。基于此&…

opencv python教程簡書_OpenCV-Python系列二:常用的圖像屬性

對于圖像&#xff0c;我們經常需要知道關于圖像的特殊屬性&#xff0c;比如寬度&#xff0c;高度&#xff0c;面積&#xff0c;像素點數目等等&#xff0c;那么在opencv-python中&#xff0c;這些信息如何獲取呢&#xff1f; 本文結構&#xff1a; 1.基本圖像屬性 2. 對于openc…

C++靜態成員函數指針

C的靜態成員函數指針 先簡單的說說非靜態的成員函數。 非靜態成員函數指針的類型&#xff1a; 類的非靜態成員是和類的對象相關的。也就是說&#xff0c;要通過類的對象來訪問變量。 成員函數的類型定義為&#xff1a; typedef void (A::*pfunc)(); A是一個類,有一個成員函數…