IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++多线程之间,线程函数启动之后,多线程依赖的启动和线程唤醒操作。 -> 正文阅读

[C++知识库]C++多线程之间,线程函数启动之后,多线程依赖的启动和线程唤醒操作。

一、原理分析

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++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-12-06 15:04:14  更:2021-12-06 15:05:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:34:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码