线程池

来源:互联网 发布:外国人看淘宝 编辑:程序博客网 时间:2024/05/22 01:00

线程池

这几天闲着没事,参考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<type> 这种定义是用来处理对象的,什么意思呢,就是说你可以在外部定义一个结构体,将你函数的输入和输出都放在这个结构体里面,然后分线程进行处理
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<Data> 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<T>;      running[i] = false;      threads[i] = std::thread(&ThreadPool::workLoop, this, i);  }

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

 void workLoop(int idx) {     std::unique_lock<std::mutex> lock(Mutex[idx]);     while(true)     {         if(running[idx] == false)             conditionVal[idx].wait(lock);         workFunc[idx]((void*)&x[idx]);         ++sleepThreadNum;         workFunc[idx] = defaultFunc<T>;         running[idx] = false;     } }

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

 bool reduce(int idx, WorkFunc f, typename Trait<T>::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();唤起线程,很简单的操作就实现了这些个功能

0 0
原创粉丝点击