线程池

来源:互联网 发布:godaddy io域名注册 编辑:程序博客网 时间:2024/04/27 13:47
 

线程池

分类: algorithm 189人阅读 评论(0) 收藏 举报
线程池

前言

     本文总结了两位前辈的博客,然后加了一点自己的理解吧。

            http://blog.csdn.net/htjoy1202/article/details/7476718

                    http://blog.csdn.net/axman/article/details/1481197


为什么创建线程池

.     所谓线程池,就是程序的初始化阶段,就预先创建一批线程,每个线程都做好准备干活。那么这样有什么好处呢。避免了每次创建线程的开销,同时线程的销废需要回收资源,也是需要开销的。而线程池省去了这些额外的线程开销,从而连续的完成所有的任务。当然,线程池中用于线程同步的操作同样也有一定的消耗,但这个消耗是相对小的。另外,还可以对线程池中的线程根据当前的任务量进行动态的调整,从而更好的节省相关资源。


线程池需要有哪些结构呢?

   (1)首先是需要有多个线程,线程池嘛,肯定是线程很多。

   (2)任务队列,这个队列可以向里面添加任务;同时,线程池里面的线程从任务队列取出任务来执行。

   (3)管理者线程,调度线程。


线程池的一个比方

【注】下面这个比方引自:http://blog.csdn.net/htjoy1202/article/details/7476718

     任务列表里面有任务了,这时候那些等待的线程们就要抢活干了,怎么抢,使用各种线程同步手段(互斥量,临界区等),人品好的线程抢到任务后,从任务列表取出任务,就可以开始干活了。干完以后,就又继续回到初始等待状态,准备抢夺下一个任务。

     这样就好比你有一批小弟排队在那里等着,一旦有任务,他们会很守纪律的去抢着干,每个任务都会被一个小弟抢走,干完以后,小弟不用休息,继续等着抢下一个任务干活。这样当你的任务源源不断的到达,你的小弟们就一个个争先恐后的抢过来完成,绝不偷懒。

     相反,如果不使用线程池,每次等到任务来了,再临时创建线程。这样就相当于每次有任务时,你再临时招聘一个小弟过来,小弟完成任务后,就回家了。然后下次再有任务,又招聘一个小弟过来,完成任务后,回家。相比线程池,中间招聘小弟的时间就要额外耗费时间和精力了(创建和销毁线程中,cpu的时间,内存的分配)。


线程池的实现

    这里详细分析下如何实现。其实这个实现模型是一个生产者与消费者模型。参考本博客:http://blog.csdn.net/lsjseu/article/details/10834981。我们知道在进程同步的时候,我们要做的第一步就是找出进程之间的前驱与后继关系,找完关系后,通过信号量来实现。在生产者-消费者模型中两层前驱关系:(1)缓冲区有物品的时候,消费者才可以消费;(2)缓冲区有空位的时候,生产者才能生产,这样才能放入缓冲区。

     同时还有一层互斥关系:缓冲区是互斥资源,保证互斥访问。


     根据这个分析,我们可以写出如下伪代码:

  1. const int MAX = 4;//缓冲区的个数  
  2. sem empty = MAX ;//生产者和消费者同步  
  3. sem full = 0;//生产者和消费这同步  
  4. sem mutex = 1;//缓冲区互斥  
  5. int in = out = 0;  
  6. producer()  
  7. {  
  8.     while(true){  
  9.         p(empty);//消费者通知生产者  
  10.         p(mutex);//保证buffer互斥访问  
  11.         in = (in + 1)%MAX;  
  12.         生产者生产放进buffer[in];  
  13.         v(mutex);  
  14.         v(full);//通知消费者可以消费  
  15.     }  
  16. }  
  17. consumer()  
  18. {  
  19.      while(true){      
  20.         p(full);//由生产者通知  
  21.         p(mutex);//保证buffer互斥访问  
  22.         out = (out + 1)%MAX;  
  23.         从buffer[out]里面消费;  
  24.         v(mutex);  
  25.         v(empty);//通知生产者可生产  
  26.      }  

     分析完生产者-消费者模型之后,再来看看本文的模型。我们直接往上面套就行了,线程池里面的线程肯定是消费者,任务队列是缓冲区,这里我们假设一个主线程是生产者。

     相当于,线程等着消费,但是必须是要任务队列中有任务,它才能消费;

     而主线程等着向任务队列加任务,但是必须要是任务队列非满(有空位置),我们才能加任务,对吧;

     同时,我们的任务队列是互斥资源,必须设置一些互斥信号量来保证互斥访问。

       下面看代码:

  1. #include <pthread.h>    
  2. #include <semaphore.h>    
  3. #include <iostream>    
  4. #include <vector>    
  5.   
  6. using namespace std;    
  7.   
  8. //任务类  
  9. class ThreadTask{    
  10. public:    
  11.     int id;    
  12.   
  13.     unsigned virtual executeThis()    
  14.     {    
  15.         return 0;    
  16.     }    
  17.   
  18.     ThreadTask(int id) : id(id) {}    
  19.     virtual ~ThreadTask(){}    
  20. };    
  21.   
  22.   
  23. class SampleThreadTask : public ThreadTask    
  24. {    
  25. public:    
  26.     unsigned virtual executeThis()    
  27.     {    
  28.         sleep(2);    
  29.         return(0);    
  30.     }    
  31.   
  32.   
  33.     SampleWorkerThread(int id) : ThreadTask(id)  
  34.     {}    
  35.   
  36.     ~SampleWorkerThread()    
  37.     {}    
  38. };


线程池类:

[cpp] view plaincopyprint?
  1. #include <pthread.h>    
  2. #include <semaphore.h>    
  3. #include <iostream>    
  4. #include <vector>    
  5. using namespace std;  
  6.   
  7. class ThreadPool{    
  8. public:    
  9.     ThreadPool();    
  10.     ThreadPool(int maxThreadsTemp);    
  11.     virtual ~ThreadPool();    
  12.   
  13.     void destroyPool(int maxPollSecs);    
  14.   
  15.     bool assignWork(ThreadTask *worker);//给线程池添加任务    
  16.     bool fetchWork(ThreadTask **worker);//线程从任务队列中取出任务执行  
  17.   
  18.     void initializeThreads();    
  19.   
  20.     static void *threadExecute(void *param);    
  21.   
  22.     static pthread_mutex_t mutexSync; //互斥  
  23.     static pthread_mutex_t mutexWorkCompletion;//互斥    
  24.   
  25.   
  26. private:    
  27.     int maxThreads;    
  28.   
  29.     pthread_cond_t  condCrit;    
  30.     sem_t availableWork;  //同步,表示当前任务队列是否有任务  
  31.     sem_t availableThreads;  //同步,表示当前任务队列是否可以加新的任务  
  32.   
  33.     vector<ThreadTask *> workerQueue; //任务队列  
  34.     int queueSize; //队列的size  
  35.     int topIndex;  //队列的头指针  
  36.     int bottomIndex;  //队列的尾指针  
  37.   
  38.     int incompleteWork; //当前队列中没有完成的任务数量  
  39. };    
  40.   
  41. //两个互斥信号量,用于进程互斥  
  42. pthread_mutex_t ThreadPool::mutexSync = PTHREAD_MUTEX_INITIALIZER;    
  43. pthread_mutex_t ThreadPool::mutexWorkCompletion = PTHREAD_MUTEX_INITIALIZER;    
  44.   
  45.   
  46.   
  47. ThreadPool::ThreadPool()    
  48. {    
  49.     ThreadPool(2);    
  50. }    
  51.   
  52. ThreadPool::ThreadPool(int maxThreads)    
  53. {    
  54.     if (maxThreads < 1)  maxThreads=1;    
  55.   
  56.     pthread_mutex_lock(&mutexSync);    
  57.     this->maxThreads = maxThreads;    
  58.     this->queueSize = maxThreads;    
  59.     workerQueue.resize(maxThreads, NULL);    
  60.     topIndex = 0;    
  61.     bottomIndex = 0;    
  62.     incompleteWork = 0;    
  63.     sem_init(&availableWork, 0, 0);    
  64.     sem_init(&availableThreads, 0, queueSize); //注意这个初始值为queueSize,刚开始队列为空   
  65.     pthread_mutex_unlock(&mutexSync);    
  66. }    
  67.   
  68. void ThreadPool::initializeThreads()    
  69. {    
  70.     for(int i = 0; i<maxThreads; ++i)    
  71.     {    
  72.         pthread_t tempThread;    
  73.         pthread_create(&tempThread, NULL, &ThreadPool::threadExecute, (void *) this );     
  74.     }    
  75.   
  76. }    
  77.   
  78. ThreadPool::~ThreadPool()    
  79. {    
  80.     workerQueue.clear();    
  81. }    
  82.   
  83. void ThreadPool::destroyPool(int maxPollSecs = 2)    
  84. {    
  85.     while( incompleteWork>0 )  //首先判断队列中的任务是否被执行完  
  86.     {    
  87.         sleep(maxPollSecs);    
  88.     }    
  89.     cout << "All Done!! Wow! That was a lot of work!" << endl;    
  90.     sem_destroy(&availableWork);    
  91.     sem_destroy(&availableThreads);    
  92.     pthread_mutex_destroy(&mutexSync);    
  93.     pthread_mutex_destroy(&mutexWorkCompletion);    
  94. }    
  95.   
  96. //向任务队列中加任务  
  97. bool ThreadPool::assignWork(ThreadTask *workerThread)    
  98. {    
  99.     pthread_mutex_lock(&mutexWorkCompletion);    
  100.     incompleteWork++;  //添加一个任务加1  
  101.     pthread_mutex_unlock(&mutexWorkCompletion);    
  102.   
  103.     sem_wait(&availableThreads);//首先判断,任务队列是不是满的,p操作
  104.       
  105.     pthread_mutex_lock(&mutexSync);  //实现队列的互斥访问
  106.     workerQueue[bottomIndex] = workerThread; //入队操作,加入任务   
  107.   
  108.     if(queueSize !=1 )    
  109.         bottomIndex = (bottomIndex+1) % (queueSize-1);    
  110.     sem_post(&availableWork);  //任务队列有任务了,线程又可以开始工作了,v操作
  111.     pthread_mutex_unlock(&mutexSync);    
  112.     return true;    
  113. }    
  114.   
  115. //线程从任务池中取任务去干  
  116. bool ThreadPool::fetchWork(ThreadTask **workerArg)    
  117. {    
  118.     sem_wait(&availableWork);  //判断任务队列有没有任务,p操作
  119.   
  120.     pthread_mutex_lock(&mutexSync);   //实现队列的互斥访问 
  121.     WorkerThread * workerThread = workerQueue[topIndex]; //出队操作   
  122.     workerQueue[topIndex] = NULL;    
  123.     *workerArg = workerThread;    
  124.     if(queueSize !=1 )    
  125.         topIndex = (topIndex+1) % (queueSize-1);    
  126.     sem_post(&availableThreads);  //取一个任务,又可以向里面加任务,v操作 
  127.     pthread_mutex_unlock(&mutexSync);    
  128.     return true;    
  129. }    
  130.   
  131. void *ThreadPool::threadExecute(void *param)    
  132. {    
  133.     ThreadTask *worker = NULL;    
  134.     //从队列中取任务  
  135.     while(((ThreadPool *)param)->fetchWork(&worker))    
  136.     {    
  137.         if(worker)    
  138.         {    
  139.             worker->executeThis();  //线程正在工作  
  140.             delete worker;    
  141.             worker = NULL;    
  142.         }    
  143.   
  144.         pthread_mutex_lock( &(((ThreadPool *)param)->mutexWorkCompletion) );    
  145.         ((ThreadPool *)param)->incompleteWork--; //完成一个任务,减1   
  146.         pthread_mutex_unlock( &(((ThreadPool *)param)->mutexWorkCompletion) );    
  147.     }    
  148.     return 0;    
  149. }    

主函数:

[cpp] view plaincopyprint?
  1. int main(int argc, char **argv)    
  2. {    
  3.     ThreadPool* myPool = new ThreadPool(25); //创建线程池对象   
  4.     myPool->initializeThreads();  //向线程池中加入线程  
  5.   
  6.     const int ITERATIONS = 200;    
  7.     for(unsigned int i=0;i<ITERATIONS;i++){  //主线程向任务队列中加任务  
  8.         SampleWorkerThread* myThread = new SampleWorkerThread(i);    
  9.         myPool->assignWork(myThread);    
  10.     }    
  11.   
  12.     myPool->destroyPool(2);    
  13.     delete myPool;    
  14.   
  15.     return 0;    
  16. }   

原创粉丝点击