C++多线程之间,线程函数启动之后,多线程依赖的启动和线程唤醒操作。
C++多線程之間,線程函數啟動之后,線程間依賴的啟動和喚醒操作
- 一、原理分析
- 1. 線程依賴關系
- 二、 實例分析
- 2.1 多線程啟動
- 2.2 多線程模式講解
- (1) 多線程開啟與主線程喚醒
- (2)單線程需要2個鎖,一個主動加鎖,一個等待互斥鎖釋放。
- (3) 運行結果展示
一、原理分析
1. 線程依賴關系
目標檢測中,為了加快速度,往往需要多線程。但是線程之間有依賴關系。然后實時控制每個線程的啟動和運行。至關重要。本文講解了,多線程函數中,線程之間有依賴關系,共享數據也有依賴。
線程ABCD。A完成后通知B,B完成業務后,通知C,C完成后,通知D,D完成后,通知A。如此循環下去。A—>B—>C—>D—>A。
我們先啟動這四個線程。每個線程兩個鎖,一個是鎖定當前的任務,讓下一個任務等待,直到該線程處理完畢,再通知下一個業務線程。 另外一個鎖是等待的鎖,用條件變量,等上一個業務線程的通知。一旦上一個業務完成后,馬上被喚醒。
對于線程B來說,需要等待A完成,等待這個鎖解鎖(A_B_mutex);此時,自己也要拿住對下一個線程相關聯的,C的鎖(B_C_mutex)。就有兩個鎖。主動擁有B–>C的鎖,被動鎖定A–>B的鎖,等待A的條件變量完成,并且被通知喚醒解鎖。
二、 實例分析
在我們的業務中。我們有四個內容,分別是發送信號SendCom(); 圖像轉換ImageConvert(); 目標檢測ObjectDetection();NMS和重采樣算法NMSResample()。
他們之間的內容,分別有相互共享的數據。且每一步,都需要依賴上一步的數據,以及等待上一步完成。
比如,目標檢測線程,需要圖像格式轉換線程完畢,才能進行圖像的檢測。NMS重采樣線程,需要對目標檢測結果(目標檢測線程完成),進行綜合分析,完成之后,將目標的位置信號發送給SendCom線程,進行采集信號發送。他們之間形成了閉環,需要由某個線程,主動啟動,才能打通依賴關系線程的開啟。
因此,我們在主線程中,主動建立了一個鎖,叫做主函數和NMS重采樣之間的鎖。通過這個鎖,我們從主線程喚醒多線程中的NMSResample()線程,從而開啟流水處理的多線程間相互依賴方式。
如下代碼中:main_scan_mutex 來開啟這個主線程多多線程的喚醒。
2.1 多線程啟動
// MultiThreadSimulation.cpp : 此文件包含 "main" 函數。程序執行將在此處開始并結束。
//#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) //第一次,等待主線程喚醒。從而開啟循環。{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.// 建立線程函數thread nms_resample_thread(threadNMSResample);thread send_com_thread(threadSendCom);thread img_cvt_thread(threadImageConvert);thread object_detect_thread(threadObjectDetection);// 主線程 解鎖并 喚醒通知 threadNMSResample 線程,開啟循環。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) 多線程開啟與主線程喚醒
下面就描述了,多線程間的依賴關系。他們之間形成了閉環。必須要主線程去主動開啟通知。 藍色的箭頭表示。
對于主線程去喚醒,我們用條件變量來通知。用的main_scan_cv來進行通知。
(2)單線程需要2個鎖,一個主動加鎖,一個等待互斥鎖釋放。
對單個線程,就需要2個鎖。比如,圖像轉換線程。他的數據,來自于發送的信號,SendCom線程處理的結果,才是需要轉換的圖像。只有圖像格式轉換完畢之后,目標檢測線程,ObjectDetection才能工作。那么,我們就需要兩個鎖定。
1:主動擁有 cvt_detect_lck。獲得cvt_detect_mutex的所有權。把下一個模塊,目標檢測目標給阻塞住。 對于當前的圖像格式轉換業務,我們一直等待 上一個模塊通知,我們就用條件變量,send_cvt_cv 來wait。直到上一個模塊,發起通知,告訴我,發送已經完成,is_send_done。
2:當is_send_done變為true。我們就不用wait,繼續執行下面的業務。所以需要send_cvt_lck。 被動擁有和輪詢send_cvt_lck的狀態解鎖。
(3) 運行結果展示
所以,我們最后的結果就是,每個模塊,都運行起來。由主函數喚醒一次。剩下就是多線程之間,互相啟動的運行過程。我們運行這個目標檢測流程5遍,然后退出。下面就是展示運行結果。
如果有用,記得點贊👍加收藏哦。!!!!
總結
以上是生活随笔為你收集整理的C++多线程之间,线程函数启动之后,多线程依赖的启动和线程唤醒操作。的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 500买猪,600卖出,700买回,80
- 下一篇: Git的安装与使用