基础篇:多线程所需知识:

前言:

  • 这里的多线程主要指算法部署时所涉及的多线程内容,对于其他多线程知识需要自行补充
  • 常用组件有thread、mutex、promise、future、condition_variable
  • 启动线程,thread,以及join、joinable、detach、类函数启动为线程
  • 生产者消费者模式
  • 队列溢出的问题,生产太快,消费太慢。如何实现溢出限制
  • 生产者如何拿到消费反馈
  • RAII+接口模式的生产者消费者封装,以及多batch的体现

1、线程:

本单元是https://blog.csdn.net/zhuangtu1999/article/details/130903594?spm=1001.2014.3001.5501#t1的实例化单元,更加注重在实战中如何运用线程知识

基础篇:多线程所需知识:_第1张图片

         上图是我们之前的一个例子,可以看到如果没有t.join(),那么线程是会崩掉的,因为他的生命周期在main()函数里面,所以当main结束之后他也会析构掉,但线程里面还在执行,所以会出现错误。

基础篇:多线程所需知识:_第2张图片

         那如果要是没有启动线程就join呢?

        可以看到也是会出错的。

所以可以发现:当线程启动了,就必须join,但如果线程没有启动,那就一定不能join,那这样肯定是很麻烦的,所以引申出来了下面的joinable概念:

joinable:

 if (t1.joinable())
    {
       t1.join();
    }

这样的话无论线程有没有启动,退出的时候都可以正常退出。

detach

 detach是分离线程,取消管理权,使线程变成野线程。但这个通常不建议使用

如果一个线程一旦detach之后,线程就交给了系统作管理,当程序结束后自动退出

基础篇:多线程所需知识:_第3张图片

 可以看到在延迟了500毫秒之后,worker down这条语句来不及打印,就会直接退出。

参数传递:

基础篇:多线程所需知识:_第4张图片

 在传递引用类型时,传入参数应用ref

 类函数:

如果我们正常想搞一个类函数私有线程和方法,那我们首先想到的是可以这样做:

class Infer{
    public:

        Infer(){

            worker_thread = thread(Infer_worker);
    
    }
    private:
    
    thread  worker_thread;
    
    void Infer_worker(){

    }
};

但这样做会报错:

 显示他不是一个静态函数,但如果我们想要把这个函数加上static

基础篇:多线程所需知识:_第5张图片

这样做的确可以成功运行,但每次都要加上sellf很麻烦。

基础篇:多线程所需知识:_第6张图片

 可以通过取地址方式,保留this参数,这样做可以避免static无法使用this->的操作。

 

2、生产者消费者模式

生产者:

queue qjobs_;

void video_capture(){
    int pic_id = 0 ;
    while (true)
    {
        /* code */
        char name[100];
        sprintf(name ,"PIC - %d" , pic_id++);
        printf("生产了一个新图片:%s\n" , name );
        qjobs_.push(name);
        this_thread::sleep_for(chrono::milliseconds(1000));
    
    }
      
}

消费者:

void infer_worker(){

    while (true)
    {
        if (!qjobs_.empty())
        {
            /* code */auto pic = qjobs_.front();
            qjobs_.pop();

            printf("消费掉一个图片:%s \n" , pic.c_str());
            this_thread::sleep_for(chrono::milliseconds(1000));
        }
        this_thread::yield();
        
    }
    
}

结果就是这样:基础篇:多线程所需知识:_第7张图片

由于生产一个图片需要1ms,消费一张图片需要1ms,所以二者刚好是收支平衡的状态。

但这个设计到了一个共享资源访问的问题,queue不是线程安全的。

这就是设计到了mutex(https://blog.csdn.net/zhuangtu1999/article/details/130917521?spm=1001.2014.3001.5501)

我们设计了一个mutex然后在创建和消费的时候都加上:

mutex lock_;
void video_capture(){
    int pic_id = 0 ;
    while (true)
    {
        /* code */
       { 
        lock_guard  l(lock_);
            char name[100];
            sprintf(name ,"PIC - %d" , pic_id++);
            printf("生产了一个新图片:%s\n" , name );
            qjobs_.push(name);
            this_thread::sleep_for(chrono::milliseconds(2000));
            
        }
    
    }
      
}

void infer_worker(){

    while (true)
    {
        if (!qjobs_.empty())
        {
                {           
                    lock_guard l(lock_);
                    auto pic = qjobs_.front();
                    qjobs_.pop();

                    printf("消费掉一个图片:%s \n" , pic.c_str());
                }
            this_thread::sleep_for(chrono::milliseconds(1000));
        }
        this_thread::yield();
        
    }
    
}

这就变成了一个原子操作。

condtion_varible:

如果生产太快,消费太慢,队列就会不断的增长,如果存储的是图片,可能就会造成显存爆炸:

基础篇:多线程所需知识:_第8张图片

 所以设置一个上限就很有必要

你可能感兴趣的:(多线程基础,c++)