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++多线程之间,线程函数启动之后,多线程依赖的启动和线程唤醒操作。相关推荐

  1. qt调用linux系统的线程函数吗,Qt之主线程与子线程通讯(linux下)

    Qt之主线程与子线程通信(linux下) 转载请注明出处:http://blog.csdn.net/feng1790291543 主线程与子线程通信主要是通过Qt上的Gui按钮,触发使得主线程上的信息 ...

  2. PYTHON——多线程:Thread类与线程函数

    Thread类与线程函数 可以使用Thread对象的join方法等待线程执行完毕:主线程(main()函数)中调用Thread对象的join方法,并且Thread对象的线程函数没有执行完毕,主线程会处 ...

  3. c++ 多线程 类成员函数_多线程(C++/Python)

    多线程(C++/Python) 本文包括一下内容: 通过C++11的标准库进行多线程编程,包括线程的创建/退出,线程管理,线程之间的通信和资源管理,以及最常见的互斥锁,另外对python下多线程的实现 ...

  4. 多线程之间如何实现同步?

    一.为什么会有线程安全问题? 线程安全问题一般是发生再多线程环境,当多个线程同时共享一个全局变量或静态变量做写的操作时候,可能会发生数据冲突问题,也就是线程安全问题,在读的操作不会发生数据冲突问题 下 ...

  5. Java 多线程之间通讯(面试概念解答三)

    多线程之间通讯 多线程之间如何实现通讯? wait().notify.notifyAll()方法 wait与sleep区别? JDK1.5-Lock Lock 接口与 synchronized 关键字 ...

  6. 【4】多线程之间实现通讯

    目录 知识点1:多线程之间如何实现通讯 1.什么是多线程之间通讯? 2.多线程之间通讯需求 3.代码实现基本实现 (1)共享资源源实体类 (2)输入线程资源 (3)输出线程 (4)运行代码 (5)解决 ...

  7. 软件安全之代码注入技术 向目标 PE 文件注入 DLL notepad lpk.dll 远程线程函数 提权函数 OpenProcess VirtualAllocEx

    实验 4 代码注入技术 引言 1.实验说明 代码注入是将用户代码注入到其他进程或者可执行文件中,实现拦截目标进程运行过程的关键信息.改变目标进程或可执行文件原本执行流程等目的 2.实验目的 本实验通过 ...

  8. 线程函数的设计以及MsgWaitForMultipleObjects函数的使用要点

    使用多线程技术可以显著地提高程序性能,本文就讲讲在程序中如何使用工作线程,以及工作线程与主线程通讯的问题. 一 创建线程 使用MFC提供的全局函数AfxBeginThread()即可创建一个工作线程. ...

  9. CreateThread创建线程函数详细讲解

    CreateThread CreateThread函数创建一个要在调用进程的地址空间中执行的线程.(MSDN讲解如下) 处理CreateThread ( LPSECURITY_ATTRIBUTES l ...

  10. linux 创建线程函数吗,[笔记]linux下和windows下的 创建线程函数

    linux下和windows下的 创建线程函数 #ifdef __GNUC__ //Linux #include #define CreateThreadEx(tid,threadFun,args) ...

最新文章

  1. pandas删除数据行中的重复数据行、基于dataframe所有列删除重复行、基于特定数据列或者列的作何删除重复行、删除重复行并保留重复行中的最后一行、pandas删除所有重复行(不进行数据保留)
  2. exe4j打包exe_Java日常实用技巧之程序打包为可执行文件
  3. MySQL Workbench 导出数据库脚本(图文)
  4. LFS、BLFS、ALFS、HLFS的区别详解
  5. LeetCode 1551. 使数组中所有元素相等的最小操作数(等差数列)
  6. android代码打开数据库,android – 如何正确关闭并重新打开Room数据库
  7. PYTHON知识梳理
  8. ClassicLink概述
  9. 装完Win8后推荐进行的优化
  10. python的自定义异常类,带参Exception,多个except,断言语句,断点,try...except,try...except...else,try...except...finally处理
  11. ai初创企业商业化落地_初创企业需要问的三个关于人工智能的问题
  12. Jmeter压力测试流程
  13. 外部修改应用程序图标的做法
  14. 《垃圾回收算法手册 自动内存管理的艺术》——引言、标记清扫(笔记)
  15. ug998逻辑思维导图
  16. ArcGIS教程 - 3 ArcGIS快速入门
  17. 服装设计师和时尚达人必看的实用网站信息大全
  18. 【汇编】汇编中的函数
  19. VMS和Windows NT的首席设计师大卫·卡特勒(David Cutler)
  20. 攻防世界 super sqli write up

热门文章

  1. 2022-2028年中国领带行业投资分析及前景预测报告
  2. python常用魔法函数
  3. pytorch之Tensor与Variable的区别
  4. CodeGen融合核心关系循环扩展
  5. 2021年大数据HBase(十六):HBase的协处理器(Coprocessor)
  6. MySQL数据库+命令大全+常用操作
  7. Make sure no other Soong process is using it
  8. Plugin with id 'com.novoda.bintray-release' not found的解决方法
  9. Oracl 12c (课本)
  10. 隐藏Nginx版本号的安全性与方法