线程池

线程池

这几天闲着没事,参考lsd-slam的线程池,做了一个更加泛化的版本,用的c++11的线程库,个人感觉还是比较强大的,拿出来和大家分享一下,代码在git上有托管,这是最新发布的1.2版本:ThreadPool


线程池的使用

这个东西是使用其实比较简单,只需要在项目里面加入ThreadPool.h头文件就可以正常使用,注意编译的时候打开-std=c++11编译flag以及-Wl,–no-as-needed链接flag

只有,你需要做的事情是定义一个ThreadPool的对象,这个对象定义有两个版本

ThreadPool<type> threads(threadNum);
ThreadPool<type pointer> threads(threadNum);

即分别是类型版本以及指针类型版本,之后请定义一个 void (*)(void*) 类型的函数,作为工作函数传给线程池,对于每个线程,可以传入不同的函数,这个函数也可以是类的成员函数,但是你需要在传入这个函数指针的同时,传入对象的指针,第三个参数如果类型是对象,那么直接传引用,如果是指针类型,那么请传入相应的指针

threads.reduce(index, workfunc, obj);
threads.reduce(index, workfunc, &obj);
threads.reduce(index, &someType::workfunc, &someTypeObj, obj);
threads.reduce(index, &someType::workfunc, &someTypeObj, &obj);

总共以上4种,注意第一个定义对应的是第一种和第三种,第二个定义对应的是第二种和第四种,那么指针类型定义和类型定义有啥区别呢,我是这么设计的

  • ThreadPool 这种定义是用来处理对象的,什么意思呢,就是说你可以在外部定义一个结构体,将你函数的输入和输出都放在这个结构体里面,然后分线程进行处理
struct Data {
   cv::Mat input;
   cv::Mat *outPut; 
   Data() {}
  ~Data() {}

   Data(Data& data) {
       input = data.input;
      outPut = data.outPut;
    }

   const Data& operator= (Data &data) {
       input = data.input;
       //cv::imshow("show", input);
       outPut = data.outPut;

       return *this;
   }
};

void testOpt(void*data_)
{   
    Data* data = (Data*)data_;
    *(data->outPut) = data->input.clone();
}


....

Data data[4];
ThreadPool thread(4);

for(int i = 0; i < 4; ++i) {
      char buff[32];
      memset(buff, 0, 32);
      sprintf(buff, "data/%d.jpg", i + 1);
      data[i].input = cv::imread(cv::String(buff));
      data[i].outPut = new cv::Mat;
}

 for(int i = 0; i < 4; ++i) {
       thread.reduce(i, testOpt, data[i]);
 }
 while(!thread.isSynchronize());
  • 对于如果是指针,这个设计类似于cuda设计,就是可以对一长串内存分段操作,像这样

    const COL = 12;
    const ROW = 12;
    void workFunc2(void*data_) {
         double *data = (double*)data_;
         for(int i = 0; i < COL; ++i)
           deal(data[i]);
    }
    double data2[ROW][COL];
    ThreadPool<double*> threads2(ROW);
    for(int i = 0; i < 12; ++i)
    threads2.reduce(i, workFunc2, data2[i]);
    

实现方案

这东西实现起来实际上挺简单的,就没几行代码,一会儿就写完了,主要思路我大概说一下

  for(int i = 0; i < maxThreadNum; ++i) {
      workFunc[i] = defaultFunc;
      running[i] = false;
      threads[i] = std::thread(&ThreadPool::workLoop, this, i);
  }

这段是构造函数里面的,实际上就是把类里面的workLoop函数放到线程里面跑,之后在workLoop里面用condition_variable来对线程进行管理

 void workLoop(int idx) {
     std::unique_lock lock(Mutex[idx]);
     while(true)
     {
         if(running[idx] == false)
             conditionVal[idx].wait(lock);

         workFunc[idx]((void*)&x[idx]);
         ++sleepThreadNum;
         workFunc[idx] = defaultFunc;
         running[idx] = false;
     }
 }

简单说就是如果running==false那么线程就等待,否则workFuncidx;调用通过reduce传进来的函数,调用完成之后,将函数设置回默认函数,最后running==false;

 bool reduce(int idx, WorkFunc f, typename Trait::reference x) {
     if(idx < 0 || idx >= maxThreadNum)
         return false;

     sourceMutex[idx].lock();
     --sleepThreadNum;
     workFunc[idx] = f;
     this->x[idx] = x;
     running[idx] = true;
     sourceMutex[idx].unlock();

     conditionVal[idx].notify_all();
     return true;
 }

这东西是reduce,实际上就是吧传入函数和数据保存起来,之后 conditionVal[idx].notify_all();唤起线程,很简单的操作就实现了这些个功能

你可能感兴趣的:(c-c++)