【TensorRT】C++多线程异步推理/部署常用代码

1.C++多线程函数的引入

#include <thread>
#include <stdio.h>
using namespace std;
void worker(int a,int& b)
{
printf("hello thread");
b=9;
}
int main()
{
//直接启动线程
//注意传入参数的生命周期:
int b=3;
//下面是对引用的处理
thread t(worker,5,std::ref(b));
t.join();//等待线程结束
//只要线程启动,就必须join
//如果没启动就join,也会报错
//故可以改为:
if(t.joinable())
{
t.join();
}
return 0;
}

分离线程,取消管理权,让其成为野线程:

int main()
{
//直接启动线程
thread t(worker);
t.join();//等待线程结束
//该线程给系统管理,直到线程退出,一般不建议使用
t.detach()
return 0;
}

2.在推理类中执行类成员函数作为多线程:

class Infer
{
public:
	infer(){
	m_worker=thread(&Infer::workerInner,this);
};
	void workerInner(){
	//内部可以使用this指针
	this->m_worker;
};
private :
     thread m_worker;
}

3.为使线程安全引入锁:共享资源访问

#include <thread>
#include <queue>
#include <mutex>
queue<int> jobs;
void video_process(){
//这里是生产者
     while(true)
     {
         jobs.push(1);
     }
}
//这里是消费者
void infer_worker(){
   while(true){
   if(!jobs.isempty())
   {
   jobs.pop();
   }
   //确保空闲时不占用过高的cpu,把当前时间片让给其它线程
   this_thread::yield();
}
}
int main()
{
thread t0(video_process);
thread t1(infer_worker);
to.join();
t1.join();
return 0;

}

注意到jobs对象不是线程安全的,所以要加锁:

#include <thread>
#include <queue>
#include <mutex>
queue<int> jobs;
mutex _lock;
void video_process(){
//这里是生产者
     while(true)
     {
     //在生产时加锁,使用大括号,限制锁的生命周期
     {
         lock_guard<mutex> l(_lock);
         jobs.push(1);
     }
     }
}
//这里是消费者
void infer_worker(){
   while(true){
   if(!jobs.isempty())
   {
   //消费时加锁:
   {
   lock_guard<mutex> l(_lock);
   jobs.pop();
   }
   }
   //确保空闲时不占用过高的cpu,把当前时间片让给其它线程
   this_thread::yield();
}
}
int main()
{
thread t0(video_process);
thread t1(infer_worker);
to.join();
t1.join();
return 0;

}

4.解决图像接收端与推理端速度不匹配的问题


当出现生产者生产太快,而消费太慢时:会让图片发生堆积,一张图片可能有2MB,如果长期运行,就会发现,程序可能在某一刻发生崩溃(内存消耗殆尽)
当队列达到上限时,进行一些操作:

#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
queue<int> jobs;
mutex _lock;
condition_variable val;
void video_process(){
//这里是生产者
     while(true)
     {
         //大括号内为锁的生存周期
         {
         //此处替换成unique_lock,自动管理锁的状态
         unique_lock<mutex> l(_lock);
         val.wait(l,[&](){
             //将在return为true时跳出等待
             //一旦进入wait解锁l,不影响消费者使用,满足条件退出wait
             //则退出加锁l。
             return jobs.size()<5;
         });
         //生产一个新的
         jobs.push(1);
         }
     }
}
//这里是消费者
void infer_worker(){
   while(true){
   if(!jobs.isempty())
   {
   //消费时加锁:
   {
   lock_guard<mutex> l(_lock);
   //通知消费掉一个,通知
   cv.notify_one();
   jobs.pop();
   }
   }
   //确保空闲时不占用过高的cpu,把当前时间片让给其它线程
   this_thread::yield();
}
}
int main()
{
thread t0(video_process);
thread t1(infer_worker);
to.join();
t1.join();
return 0;

}

5.一张图片,经多个模型推理的异步设计


下面开始将消费者消费后的推理结果,送回到消费者处:
(因为消费者不止一个,可能有多个网络进行推理,要把推理结果送还到图片接收处)。这样多个推理就可以做成异步模式。实际上是调用逻辑与推理逻辑分离,简化了接口,优化了性能。

#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <memory>
#include <future>
//为方便某些数据复制,引入了指针
struct work2do
{
    //假定其具有int类型的返回值
    shared_ptr<promise<int>> returnValue;
    int input;
};
queue<work2do> jobque;
mutex _lock;
condition_variable val;
void video_process(){
//这里是生产者
     while(true)
     {
          work2do work;
         {
         //此处替换成unique_lock,自动管理锁的状态
         unique_lock<mutex> l(_lock);
         val.wait(l,[&](){
             //将在return为true时跳出等待
             //一旦进入wait解锁l,不影响消费者使用,满足条件退出wait
             //则退出加锁l。
             return jobs.size()<5;
         });
         
         work.returnValue.reset(new promise<int>())
         work.input=1;
         jobque.push(work);
         }
         //进行了线程推理后,等待,直到在消费者处触发promise.set_value
         //其中.get()被调用后就开始等待。
         //注意,接收返回值代码不能放入锁的生存周期之内,在进入get()的等待前,锁就应解锁。
         work.returnValue->get_future().get()
     }
}
//这里是消费者
void infer_worker(){
   while(true){
   if(!jobque.isempty())
   {
   //消费时加锁:
   {
   lock_guard<mutex> l(_lock);
   auto curjob=jobque.front();
   jobque.pop();
   //通知消费掉一个,通知
   cv.notify_one();
   //设置新的返回值:
   curjob.returnValue.set_value(5);
   }
   }
   //确保空闲时不占用过高的cpu,把当前时间片让给其它线程
   this_thread::yield();
}
}
int main()
{
thread t0(video_process);
thread t1(infer_worker);
to.join();
t1.join();
return 0;

}

  • 7
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

颢师傅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值