C++多线程之间,线程函数启动之后,多线程依赖的启动和线程唤醒操作。
C++多線程之間,線程函數(shù)啟動之后,線程間依賴的啟動和喚醒操作
- 一、原理分析
- 1. 線程依賴關(guān)系
- 二、 實例分析
- 2.1 多線程啟動
- 2.2 多線程模式講解
- (1) 多線程開啟與主線程喚醒
- (2)單線程需要2個鎖,一個主動加鎖,一個等待互斥鎖釋放。
- (3) 運(yùn)行結(jié)果展示
一、原理分析
1. 線程依賴關(guān)系
目標(biāo)檢測中,為了加快速度,往往需要多線程。但是線程之間有依賴關(guān)系。然后實時控制每個線程的啟動和運(yùn)行。至關(guān)重要。本文講解了,多線程函數(shù)中,線程之間有依賴關(guān)系,共享數(shù)據(jù)也有依賴。
線程ABCD。A完成后通知B,B完成業(yè)務(wù)后,通知C,C完成后,通知D,D完成后,通知A。如此循環(huán)下去。A—>B—>C—>D—>A。
我們先啟動這四個線程。每個線程兩個鎖,一個是鎖定當(dāng)前的任務(wù),讓下一個任務(wù)等待,直到該線程處理完畢,再通知下一個業(yè)務(wù)線程。 另外一個鎖是等待的鎖,用條件變量,等上一個業(yè)務(wù)線程的通知。一旦上一個業(yè)務(wù)完成后,馬上被喚醒。
對于線程B來說,需要等待A完成,等待這個鎖解鎖(A_B_mutex);此時,自己也要拿住對下一個線程相關(guān)聯(lián)的,C的鎖(B_C_mutex)。就有兩個鎖。主動擁有B–>C的鎖,被動鎖定A–>B的鎖,等待A的條件變量完成,并且被通知喚醒解鎖。
二、 實例分析
在我們的業(yè)務(wù)中。我們有四個內(nèi)容,分別是發(fā)送信號SendCom(); 圖像轉(zhuǎn)換ImageConvert(); 目標(biāo)檢測ObjectDetection();NMS和重采樣算法NMSResample()。
他們之間的內(nèi)容,分別有相互共享的數(shù)據(jù)。且每一步,都需要依賴上一步的數(shù)據(jù),以及等待上一步完成。
比如,目標(biāo)檢測線程,需要圖像格式轉(zhuǎn)換線程完畢,才能進(jìn)行圖像的檢測。NMS重采樣線程,需要對目標(biāo)檢測結(jié)果(目標(biāo)檢測線程完成),進(jìn)行綜合分析,完成之后,將目標(biāo)的位置信號發(fā)送給SendCom線程,進(jìn)行采集信號發(fā)送。他們之間形成了閉環(huán),需要由某個線程,主動啟動,才能打通依賴關(guān)系線程的開啟。
因此,我們在主線程中,主動建立了一個鎖,叫做主函數(shù)和NMS重采樣之間的鎖。通過這個鎖,我們從主線程喚醒多線程中的NMSResample()線程,從而開啟流水處理的多線程間相互依賴方式。
如下代碼中:main_scan_mutex 來開啟這個主線程多多線程的喚醒。
2.1 多線程啟動
// MultiThreadSimulation.cpp : 此文件包含 "main" 函數(shù)。程序執(zhí)行將在此處開始并結(jié)束。
//#include <iostream>
#include <time.h>
#include <thread>
#include <condition_variable>using namespace std;/* condition lock, and mutex for thread controlling. */
mutex send_cvt_mutex,cvt_detect_mutex, detect_nms_mutex, nms_send_mutex, main_scan_mutex;
condition_variable send_cvt_cv, cvt_detect_cv, detect_nms_cv, nms_send_cv, main_scan_cv;bool is_send_done = false;
bool is_cvt_done = false;
bool is_detect_done = false;
bool is_nms_done = false;
int total_scan_time = 0;void threadSendCom()
{cout << "1. Send Com Thread!" << endl;while (total_scan_time < 5){this_thread::sleep_for(chrono::microseconds(2));unique_lock<mutex> send_cvt_lck(send_cvt_mutex);unique_lock<mutex> nms_send_lck(nms_send_mutex);nms_send_cv.wait(nms_send_lck, [] {return is_nms_done; });cout << " >>>> Is send com: " << total_scan_time << endl;this_thread::sleep_for(chrono::milliseconds(2));is_send_done = true;send_cvt_cv.notify_one();is_nms_done = false;}cout << " Thread 1 end!" << endl;
}void threadImageConvert()
{cout << "2. Img convert Thread!" << endl;while (total_scan_time < 5){this_thread::sleep_for(chrono::microseconds(2));unique_lock<mutex> cvt_detect_lck(cvt_detect_mutex);unique_lock<mutex> send_cvt_lck(send_cvt_mutex);send_cvt_cv.wait(send_cvt_lck, [] {return is_send_done; });cout << " >>>> Is convert image: " << total_scan_time << endl;this_thread::sleep_for(chrono::milliseconds(2));is_cvt_done = true;cvt_detect_cv.notify_one();send_cvt_lck.unlock();is_send_done = false;}cout << "Thread 2 end!" << endl;
}void threadObjectDetection()
{cout << "3. Object detect Thread!" << endl;while (total_scan_time < 5){this_thread::sleep_for(chrono::microseconds(2));unique_lock<mutex> detect_nms_lck(detect_nms_mutex);unique_lock<mutex> cvt_detect_lck(cvt_detect_mutex);cvt_detect_cv.wait(cvt_detect_lck, [] {return is_cvt_done; });cout << " >>>>Is object detect: " << total_scan_time << endl;this_thread::sleep_for(chrono::milliseconds(2));is_detect_done = true;detect_nms_cv.notify_one();cvt_detect_lck.unlock();is_cvt_done = false;}cout << "Thread 3 end!" << endl;
}void threadNMSResample()
{cout << "4. Nms and resample Thread!" << endl;while (total_scan_time < 5){this_thread::sleep_for(chrono::microseconds(2));unique_lock<mutex> nms_send_lck(nms_send_mutex);if (total_scan_time == 0) //第一次,等待主線程喚醒。從而開啟循環(huán)。{unique_lock<mutex> main_scan_lck(main_scan_mutex);cout << "!!! Waiting here for main thread unlock mutex" << endl;main_scan_cv.wait(main_scan_lck, [] {return is_detect_done; });}else{unique_lock<mutex> detect_nms_lck(detect_nms_mutex);detect_nms_cv.wait(detect_nms_lck, [] {return is_detect_done; });}cout << " >>>> Is NMS resample:" << total_scan_time +1 << endl;this_thread::sleep_for(chrono::milliseconds(2));is_nms_done = true;nms_send_cv.notify_one();is_detect_done = false;total_scan_time += 1;}cout << "Thread 4 end!" << endl;
}int main()
{// Resample --> SendCom --> ImgConvert --> ObjectDetection.// 建立線程函數(shù)thread nms_resample_thread(threadNMSResample);thread send_com_thread(threadSendCom);thread img_cvt_thread(threadImageConvert);thread object_detect_thread(threadObjectDetection);// 主線程 解鎖并 喚醒通知 threadNMSResample 線程,開啟循環(huán)。this_thread::sleep_for(chrono::milliseconds(100));{cout << ">>>> Main thread begin!!!!!!" << endl;unique_lock<mutex> main_scan_lck(main_scan_mutex);is_detect_done = true;}main_scan_cv.notify_one();nms_resample_thread.join();object_detect_thread.join();img_cvt_thread.join();send_com_thread.join();cout << " End all" << endl;}
2.2 多線程模式講解
(1) 多線程開啟與主線程喚醒
下面就描述了,多線程間的依賴關(guān)系。他們之間形成了閉環(huán)。必須要主線程去主動開啟通知。 藍(lán)色的箭頭表示。
對于主線程去喚醒,我們用條件變量來通知。用的main_scan_cv來進(jìn)行通知。
(2)單線程需要2個鎖,一個主動加鎖,一個等待互斥鎖釋放。
對單個線程,就需要2個鎖。比如,圖像轉(zhuǎn)換線程。他的數(shù)據(jù),來自于發(fā)送的信號,SendCom線程處理的結(jié)果,才是需要轉(zhuǎn)換的圖像。只有圖像格式轉(zhuǎn)換完畢之后,目標(biāo)檢測線程,ObjectDetection才能工作。那么,我們就需要兩個鎖定。
1:主動擁有 cvt_detect_lck。獲得cvt_detect_mutex的所有權(quán)。把下一個模塊,目標(biāo)檢測目標(biāo)給阻塞住。 對于當(dāng)前的圖像格式轉(zhuǎn)換業(yè)務(wù),我們一直等待 上一個模塊通知,我們就用條件變量,send_cvt_cv 來wait。直到上一個模塊,發(fā)起通知,告訴我,發(fā)送已經(jīng)完成,is_send_done。
2:當(dāng)is_send_done變?yōu)閠rue。我們就不用wait,繼續(xù)執(zhí)行下面的業(yè)務(wù)。所以需要send_cvt_lck。 被動擁有和輪詢send_cvt_lck的狀態(tài)解鎖。
(3) 運(yùn)行結(jié)果展示
所以,我們最后的結(jié)果就是,每個模塊,都運(yùn)行起來。由主函數(shù)喚醒一次。剩下就是多線程之間,互相啟動的運(yùn)行過程。我們運(yùn)行這個目標(biāo)檢測流程5遍,然后退出。下面就是展示運(yùn)行結(jié)果。
如果有用,記得點贊👍加收藏哦。!!!!
總結(jié)
以上是生活随笔為你收集整理的C++多线程之间,线程函数启动之后,多线程依赖的启动和线程唤醒操作。的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 500买猪,600卖出,700买回,80
- 下一篇: Git的安装与使用