分享

线程池的原理和实现

 蜗牛an 2013-07-31

分类: linux内核与编程 C vs C++
 136人阅读 评论(0) 收藏 举报
线程池的原理和实现
    
一. 线程池的简介
       通常我们使用多线程的方式是,需要时创建一个新的线程,在这个线程里执行特定的任务,然后在任务完成后退出。这在一般的应用里已经能够满足我们应用的需求,毕竟我们并不是什么时候都需要创建大量的线程,并在它们执行一个简单的任务后销毁。

         但是在一些web、email、database等应用里,比如彩铃,我们的应用在任何时候都要准备应对数目巨大的连接请求,同时,这些请求所要完成的任务却又可能非常的简单,即只占用很少的处理时间。这时,我们的应用有可能处于不停的创建线程并销毁线程的状态。虽说比起进程的创建,线程的创建时间已经大大缩短,但是如果需要频繁的创建线程,并且每个线程所占用的处理时间又非常简短,则线程创建和销毁带给处理器的额外负担也是很可观的。

         线程池的作用正是在这种情况下有效的降低频繁创建销毁线程所带来的额外开销。一般来说,线程池都是采用预创建的技术,在应用启动之初便预先创建一定数目的线程。应用在运行的过程中,需要时可以从这些线程所组成的线程池里申请分配一个空闲的线程,来执行一定的任务,任务完成后,并不是将线程销毁,而是将它返还给线程池,由线程池自行管理。如果线程池中预先分配的线程已经全部分配完毕,但此时又有新的任务请求,则线程池会动态的创建新的线程去适应这个请求。当然,有可能,某些时段应用并不需要执行很多的任务,导致了线程池中的线程大多处于空闲的状态,为了节省系统资源,线程池就需要动态的销毁其中的一部分空闲线程。因此,线程池都需要一个管理者,按照一定的要求去动态的维护其中线程的数目。

        基于上面的技术,线程池将频繁创建和销毁线程所带来的开销分摊到了每个具体执行的任务上,执行的次数越多,则分摊到每个任务上的开销就越小。

        当然,如果线程创建销毁所带来的开销与线程执行任务的开销相比微不足道,可以忽略不计,则线程池并没有使用的必要。比如,FTP、Telnet等应用时。

二. 线程池的设计
       下面利用C语言来实现一个简单的线程池,为了使得这个线程池库使用起来更加方便,特在C实现中加入了一些OO的思想,与Objective-C不同,它仅仅是使用了struct来模拟了c++中的类,其实这种方式在linux内核中大量可见。
       在这个库里,与用户有关的接口主要有:

       typedef struct tp_work_desc_s tp_work_desc; //应用线程执行任务时所需要的一些信息

       typedef struct tp_work_s tp_work; //线程执行的任务

       typedef struct tp_thread_info_s tp_thread_info; //描述了各个线程id,是否空闲,执行的任务等信息

       typedef struct tp_thread_pool_s tp_thread_pool; // 有关线程池操作的接口信息

         //thread parm

       struct tp_work_desc_s{

                  ……

        };

       //base thread struct

       struct tp_work_s{

                  //main process function. user interface

                  void (*process_job)(tp_work *this, tp_work_desc *job);

        };

        tp_thread_pool *creat_thread_pool(int min_num, int max_num);

        tp_work_desc_s表示应用线程执行任务时所需要的一些信息,会被当作线程的参数传递给每个线程,依据应用的不同而不同,需要用户定义结构的内容。tp_work_s就是我们希望线程执行的任务了。当我们申请分配一个新的线程时,首先要明确的指定这两个结构,即该线程完成什么任务,并且完成这个任务需要哪些额外的信息。接口函数creat_thread_pool用来创建一个线程池的实例,使用时需要指定该线程池实例所能容纳的最小线程数min_num和最大线程数max_num。最小线程数即线程池创建时预创建的线程数目,这个数目的大小也直接影响了线程池所能起到的效果,如果指定的太小,线程池中预创建的线程很快就将分配完毕并需要创建新的线程来适应不断的请求,如果指定的太大,则将可能会有大量的空闲线程。我们需要根据自己应用的实际需要进行指定。描述线程池的结构如下:

        //main thread pool struct

        struct tp_thread_pool_s{

             TPBOOL (*init)(tp_thread_pool *this);

             void (*close)(tp_thread_pool *this);

             void (*process_job)(tp_thread_pool *this, tp_work *worker, tp_work_desc *job);

             int  (*get_thread_by_id)(tp_thread_pool *this, int id);

             TPBOOL (*add_thread)(tp_thread_pool *this);

             TPBOOL (*delete_thread)(tp_thread_pool *this);

              int (*get_tp_status)(tp_thread_pool *this); 

              int min_th_num;                //min thread number in the pool

              int cur_th_num;                 //current thread number in the pool

              int max_th_num;         //max thread number in the pool

              pthread_mutex_t tp_lock;

              pthread_t manage_thread_id;  //manage thread id num

              tp_thread_info *thread_info;   //work thread relative thread info

};

         结构tp_thread_info_s描述了各个线程id、是否空闲、执行的任务等信息,用户并不需要关心它。

         //thread info

         struct tp_thread_info_s{

              pthread_t          thread_id;         //thread id num

             TPBOOL                   is_busy;    //thread status:true-busy;flase-idle

             pthread_cond_t          thread_cond;

             pthread_mutex_t               thread_lock;

             tp_work                      *th_work;

             tp_work_desc            *th_job;

         };

       tp_thread_pool_s结构包含了有关线程池操作的接口和变量。在使用creat_thread_pool返回一个线程池实例之后,首先要使用明确使用init接口对它进行初始化。在这个初始化过程中,线程池会预创建指定的最小线程数目的线程,它们都处于阻塞状态,并不损耗CPU,但是会占用一定的内存空间。同时init也会创建一个线程池的管理线程,这个线程会在线程池的运行周期内一直执行,它将定时的查看分析线程池的状态,如果线程池中空闲的线程过多,它会删除部分空闲的线程,当然它并不会使所有线程的数目小于指定的最小线程数。

         在已经创建并初始化了线程池之后,我们就可以指定tp_work_desc_stp_work_s结构,并使用线程池的process_job接口来执行它们。这些就是我们使用这个线程池时所需要了解的所有东西。如果不再需要线程池,可以使用close接口销毁它。

三. 实现代码

Thread-pool.h(头文件):

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <sys/types.h>  
  4. #include <pthread.h>  
  5. #include <signal.h>  
  6.   
  7. #ifndef TPBOOL  
  8. typedef int TPBOOL;  
  9. #endif  
  10.   
  11. #ifndef TRUE  
  12. #define TRUE 1  
  13. #endif  
  14.   
  15. #ifndef FALSE  
  16. #define FALSE 0  
  17. #endif  
  18.   
  19. #define BUSY_THRESHOLD 0.5  //(busy thread)/(all thread threshold)  
  20. #define MANAGE_INTERVAL 5   //tp manage thread sleep interval  
  21.   
  22. typedef struct tp_work_desc_s tp_work_desc;  
  23. typedef struct tp_work_s tp_work;  
  24. typedef struct tp_thread_info_s tp_thread_info;  
  25. typedef struct tp_thread_pool_s tp_thread_pool;  
  26.   
  27. //thread parm  
  28. struct tp_work_desc_s{  
  29.     char *inum; //call in  
  30.     char *onum; //call out  
  31.     int chnum;  //channel num  
  32. };  
  33.   
  34. //base thread struct  
  35. struct tp_work_s{  
  36.     //main process function. user interface  
  37.     void (*process_job)(tp_work *this, tp_work_desc *job);  
  38. };  
  39.   
  40. //thread info  
  41. struct tp_thread_info_s{  
  42.     pthread_t       thread_id;  //thread id num  
  43.     TPBOOL          is_busy;    //thread status:true-busy;flase-idle  
  44.     pthread_cond_t          thread_cond;      
  45.     pthread_mutex_t     thread_lock;  
  46.     tp_work         *th_work;  
  47.     tp_work_desc        *th_job;  
  48. };  
  49.   
  50. //main thread pool struct  
  51. struct tp_thread_pool_s{  
  52.     TPBOOL (*init)(tp_thread_pool *this);  
  53.     void (*close)(tp_thread_pool *this);  
  54.     void (*process_job)(tp_thread_pool *this, tp_work *worker, tp_work_desc *job);  
  55.     int  (*get_thread_by_id)(tp_thread_pool *thisint id);  
  56.     TPBOOL (*add_thread)(tp_thread_pool *this);  
  57.     TPBOOL (*delete_thread)(tp_thread_pool *this);  
  58.     int (*get_tp_status)(tp_thread_pool *this);  
  59.       
  60.     int min_th_num;     //min thread number in the pool  
  61.     int cur_th_num;     //current thread number in the pool  
  62.     int max_th_num;         //max thread number in the pool  
  63.     pthread_mutex_t tp_lock;  
  64.     pthread_t manage_thread_id; //manage thread id num  
  65.     tp_thread_info *thread_info;    //work thread relative thread info  
  66. };  
  67.   
  68. tp_thread_pool *creat_thread_pool(int min_num, int max_num);  

Thread-pool.c(实现文件):

  1. #include "thread-pool.h"  
  2.   
  3. static void *tp_work_thread(void *pthread);  
  4. static void *tp_manage_thread(void *pthread);  
  5.   
  6. static TPBOOL tp_init(tp_thread_pool *this);  
  7. static void tp_close(tp_thread_pool *this);  
  8. static void tp_process_job(tp_thread_pool *this, tp_work *worker, tp_work_desc *job);  
  9. static int  tp_get_thread_by_id(tp_thread_pool *thisint id);  
  10. static TPBOOL tp_add_thread(tp_thread_pool *this);  
  11. static TPBOOL tp_delete_thread(tp_thread_pool *this);  
  12. static int  tp_get_tp_status(tp_thread_pool *this);  
  13.   
  14. /** 
  15.   * user interface. creat thread pool. 
  16.   * para: 
  17.   *     num: min thread number to be created in the pool 
  18.   * return: 
  19.   *     thread pool struct instance be created successfully 
  20.   */  
  21. tp_thread_pool *creat_thread_pool(int min_num, int max_num){  
  22.     tp_thread_pool *this;  
  23.     this = (tp_thread_pool*)malloc(sizeof(tp_thread_pool));   
  24.   
  25.     memset(this, 0, sizeof(tp_thread_pool));  
  26.       
  27.     //init member function ponter  
  28.     this->init = tp_init;  
  29.     this->close = tp_close;  
  30.     this->process_job = tp_process_job;  
  31.     this->get_thread_by_id = tp_get_thread_by_id;  
  32.     this->add_thread = tp_add_thread;  
  33.     this->delete_thread = tp_delete_thread;  
  34.     this->get_tp_status = tp_get_tp_status;  
  35.   
  36.     //init member var  
  37.     this->min_th_num = min_num;  
  38.     this->cur_th_num = this->min_th_num;  
  39.     this->max_th_num = max_num;  
  40.     pthread_mutex_init(&this->tp_lock, NULL);  
  41.   
  42.     //malloc mem for num thread info struct  
  43.     if(NULL != this->thread_info)  
  44.         free(this->thread_info);  
  45.     this->thread_info = (tp_thread_info*)malloc(sizeof(tp_thread_info)*this->max_th_num);  
  46.   
  47.     return this;  
  48. }  
  49.   
  50.   
  51. /** 
  52.   * member function reality. thread pool init function. 
  53.   * para: 
  54.   *     this: thread pool struct instance ponter 
  55.   * return: 
  56.   *     true: successful; false: failed 
  57.   */  
  58. TPBOOL tp_init(tp_thread_pool *this){  
  59.     int i;  
  60.     int err;  
  61.       
  62.     //creat work thread and init work thread info  
  63.     for(i=0;i<this->min_th_num;i++){  
  64.         pthread_cond_init(&this->thread_info[i].thread_cond, NULL);  
  65.         pthread_mutex_init(&this->thread_info[i].thread_lock, NULL);  
  66.           
  67.         err = pthread_create(&this->thread_info[i].thread_id, NULL, tp_work_thread, this);  
  68.         if(0 != err){  
  69.             printf("tp_init: creat work thread failed\n");  
  70.             return FALSE;  
  71.         }  
  72.         printf("tp_init: creat work thread %d\n"this->thread_info[i].thread_id);  
  73.     }  
  74.   
  75.     //creat manage thread  
  76.     err = pthread_create(&this->manage_thread_id, NULL, tp_manage_thread, this);  
  77.     if(0 != err){  
  78.         printf("tp_init: creat manage thread failed\n");  
  79.         return FALSE;  
  80.     }  
  81.     printf("tp_init: creat manage thread %d\n"this->manage_thread_id);  
  82.   
  83.     return TRUE;  
  84. }  
  85.   
  86. /** 
  87.   * member function reality. thread pool entirely close function. 
  88.   * para: 
  89.   *     this: thread pool struct instance ponter 
  90.   * return: 
  91.   */  
  92. void tp_close(tp_thread_pool *this){  
  93.     int i;  
  94.       
  95.     //close work thread  
  96.     for(i=0;i<this->cur_th_num;i++){  
  97.         kill(this->thread_info[i].thread_id, SIGKILL);  
  98.         pthread_mutex_destroy(&this->thread_info[i].thread_lock);  
  99.         pthread_cond_destroy(&this->thread_info[i].thread_cond);  
  100.         printf("tp_close: kill work thread %d\n"this->thread_info[i].thread_id);  
  101.     }  
  102.   
  103.     //close manage thread  
  104.     kill(this->manage_thread_id, SIGKILL);  
  105.     pthread_mutex_destroy(&this->tp_lock);  
  106.     printf("tp_close: kill manage thread %d\n"this->manage_thread_id);  
  107.       
  108.     //free thread struct  
  109.     free(this->thread_info);  
  110. }  
  111.   
  112. /** 
  113.   * member function reality. main interface opened.  
  114.   * after getting own worker and job, user may use the function to process the task. 
  115.   * para: 
  116.   *     this: thread pool struct instance ponter 
  117.   * worker: user task reality. 
  118.   * job: user task para 
  119.   * return: 
  120.   */  
  121. void tp_process_job(tp_thread_pool *this, tp_work *worker, tp_work_desc *job){  
  122.     int i;  
  123.     int tmpid;  
  124.   
  125.     //fill this->thread_info's relative work key  
  126.     for(i=0;i<this->cur_th_num;i++){  
  127.         pthread_mutex_lock(&this->thread_info[i].thread_lock);  
  128.         if(!this->thread_info[i].is_busy){  
  129.             printf("tp_process_job: %d thread idle, thread id is %d\n", i, this->thread_info[i].thread_id);  
  130.             //thread state be set busy before work  
  131.             this->thread_info[i].is_busy = TRUE;  
  132.             pthread_mutex_unlock(&this->thread_info[i].thread_lock);  
  133.               
  134.             this->thread_info[i].th_work = worker;  
  135.             this->thread_info[i].th_job = job;  
  136.               
  137.             printf("tp_process_job: informing idle working thread %d, thread id is %d\n", i, this->thread_info[i].thread_id);  
  138.             pthread_cond_signal(&this->thread_info[i].thread_cond);  
  139.   
  140.             return;  
  141.         }  
  142.         else   
  143.             pthread_mutex_unlock(&this->thread_info[i].thread_lock);       
  144.     }//end of for  
  145.   
  146.     //if all current thread are busy, new thread is created here  
  147.     pthread_mutex_lock(&this->tp_lock);  
  148.     ifthis->add_thread(this) ){  
  149.         i = this->cur_th_num - 1;  
  150.         tmpid = this->thread_info[i].thread_id;  
  151.         this->thread_info[i].th_work = worker;  
  152.         this->thread_info[i].th_job = job;  
  153.     }  
  154.     pthread_mutex_unlock(&this->tp_lock);  
  155.       
  156.     //send cond to work thread  
  157.     printf("tp_process_job: informing idle working thread %d, thread id is %d\n", i, this->thread_info[i].thread_id);  
  158.     pthread_cond_signal(&this->thread_info[i].thread_cond);  
  159.     return;   
  160. }  
  161.   
  162. /** 
  163.   * member function reality. get real thread by thread id num. 
  164.   * para: 
  165.   *     this: thread pool struct instance ponter 
  166.   * id: thread id num 
  167.   * return: 
  168.   *     seq num in thread info struct array 
  169.   */  
  170. int tp_get_thread_by_id(tp_thread_pool *thisint id){  
  171.     int i;  
  172.   
  173.     for(i=0;i<this->cur_th_num;i++){  
  174.         if(id == this->thread_info[i].thread_id)  
  175.             return i;  
  176.     }  
  177.   
  178.     return -1;  
  179. }  
  180.   
  181. /** 
  182.   * member function reality. add new thread into the pool. 
  183.   * para: 
  184.   *     this: thread pool struct instance ponter 
  185.   * return: 
  186.   *     true: successful; false: failed 
  187.   */  
  188. static TPBOOL tp_add_thread(tp_thread_pool *this){  
  189.     int err;  
  190.     tp_thread_info *new_thread;  
  191.       
  192.     ifthis->max_th_num <= this->cur_th_num )  
  193.         return FALSE;  
  194.           
  195.     //malloc new thread info struct  
  196.     new_thread = &this->thread_info[this->cur_th_num];  
  197.       
  198.     //init new thread's cond & mutex  
  199.     pthread_cond_init(&new_thread->thread_cond, NULL);  
  200.     pthread_mutex_init(&new_thread->thread_lock, NULL);  
  201.   
  202.     //init status is busy  
  203.     new_thread->is_busy = TRUE;  
  204.   
  205.     //add current thread number in the pool.  
  206.     this->cur_th_num++;  
  207.       
  208.     err = pthread_create(&new_thread->thread_id, NULL, tp_work_thread, this);  
  209.     if(0 != err){  
  210.         free(new_thread);  
  211.         return FALSE;  
  212.     }  
  213.     printf("tp_add_thread: creat work thread %d\n"this->thread_info[this->cur_th_num-1].thread_id);  
  214.       
  215.     return TRUE;  
  216. }  
  217.   
  218. /** 
  219.   * member function reality. delete idle thread in the pool. 
  220.   * only delete last idle thread in the pool. 
  221.   * para: 
  222.   *     this: thread pool struct instance ponter 
  223.   * return: 
  224.   *     true: successful; false: failed 
  225.   */  
  226. static TPBOOL tp_delete_thread(tp_thread_pool *this){  
  227.     //current thread num can't < min thread num  
  228.     if(this->cur_th_num <= this->min_th_num) return FALSE;  
  229.   
  230.     //if last thread is busy, do nothing  
  231.     if(this->thread_info[this->cur_th_num-1].is_busy) return FALSE;  
  232.   
  233.     //kill the idle thread and free info struct  
  234.     kill(this->thread_info[this->cur_th_num-1].thread_id, SIGKILL);  
  235.     pthread_mutex_destroy(&this->thread_info[this->cur_th_num-1].thread_lock);  
  236.     pthread_cond_destroy(&this->thread_info[this->cur_th_num-1].thread_cond);  
  237.   
  238.     //after deleting idle thread, current thread num -1  
  239.     this->cur_th_num--;  
  240.   
  241.     return TRUE;  
  242. }  
  243.   
  244. /** 
  245.   * member function reality. get current thread pool status:idle, normal, busy, .etc. 
  246.   * para: 
  247.   *     this: thread pool struct instance ponter 
  248.   * return: 
  249.   *     0: idle; 1: normal or busy(don't process) 
  250.   */  
  251. static int  tp_get_tp_status(tp_thread_pool *this){  
  252.     float busy_num = 0.0;  
  253.     int i;  
  254.   
  255.     //get busy thread number  
  256.     for(i=0;i<this->cur_th_num;i++){  
  257.         if(this->thread_info[i].is_busy)  
  258.             busy_num++;  
  259.     }  
  260.   
  261.     //0.2? or other num?  
  262.     if(busy_num/(this->cur_th_num) < BUSY_THRESHOLD)  
  263.         return 0;//idle status  
  264.     else  
  265.         return 1;//busy or normal status      
  266. }  
  267.   
  268. /** 
  269.   * internal interface. real work thread. 
  270.   * para: 
  271.   *     pthread: thread pool struct ponter 
  272.   * return: 
  273.   */  
  274. static void *tp_work_thread(void *pthread){  
  275.     pthread_t curid;//current thread id  
  276.     int nseq;//current thread seq in the this->thread_info array  
  277.     tp_thread_pool *this = (tp_thread_pool*)pthread;//main thread pool struct instance  
  278.   
  279.     //get current thread id  
  280.     curid = pthread_self();  
  281.       
  282.     //get current thread's seq in the thread info struct array.  
  283.     nseq = this->get_thread_by_id(this, curid);  
  284.     if(nseq < 0)  
  285.         return;  
  286.     printf("entering working thread %d, thread id is %d\n", nseq, curid);  
  287.   
  288.     //wait cond for processing real job.  
  289.     while( TRUE ){  
  290.         pthread_mutex_lock(&this->thread_info[nseq].thread_lock);  
  291.         pthread_cond_wait(&this->thread_info[nseq].thread_cond, &this->thread_info[nseq].thread_lock);  
  292.         pthread_mutex_unlock(&this->thread_info[nseq].thread_lock);        
  293.           
  294.         printf("%d thread do work!\n", pthread_self());  
  295.   
  296.         tp_work *work = this->thread_info[nseq].th_work;  
  297.         tp_work_desc *job = this->thread_info[nseq].th_job;  
  298.   
  299.         //process  
  300.         work->process_job(work, job);  
  301.   
  302.         //thread state be set idle after work  
  303.         pthread_mutex_lock(&this->thread_info[nseq].thread_lock);          
  304.         this->thread_info[nseq].is_busy = FALSE;  
  305.         pthread_mutex_unlock(&this->thread_info[nseq].thread_lock);  
  306.           
  307.         printf("%d do work over\n", pthread_self());  
  308.     }     
  309. }  
  310.   
  311. /** 
  312.   * internal interface. manage thread pool to delete idle thread. 
  313.   * para: 
  314.   *     pthread: thread pool struct ponter 
  315.   * return: 
  316.   */  
  317. static void *tp_manage_thread(void *pthread){  
  318.     tp_thread_pool *this = (tp_thread_pool*)pthread;//main thread pool struct instance  
  319.   
  320.     //1?  
  321.     sleep(MANAGE_INTERVAL);  
  322.   
  323.     do{  
  324.         ifthis->get_tp_status(this) == 0 ){  
  325.             do{  
  326.                 if( !this->delete_thread(this) )  
  327.                     break;  
  328.             }while(TRUE);  
  329.         }//end for if  
  330.   
  331.         //1?  
  332.         sleep(MANAGE_INTERVAL);  
  333.     }while(TRUE);  
  334. }  
四. 数据库连接池介绍   

      数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。

      一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的 性能低下。 数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并讲这些连接组成一个连接池(简单说:在一个“池”里放了好多半成品的数据库联接对象),由应用程序动态地对池中的连接进行申请、使用和释放。对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。 

      连接池技术尽可能多地重用了消耗内存地资源,大大节省了内存,提高了服务器地服务效率,能够支持更多的客户服务。通过使用连接池,将大大提高程序运行效率,同时,我们可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。

     1)  最小连接数是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费; 
     2)  最大连接数是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会影响之后的数据库操作。

参考资料:

1.  libthreadpool库的实现:

2.  http://www.cnblogs.com/cute/archive/2012/09/18/2690616.html

3.  《线程池技术在并发服务器中的应用》——计算机与数字工程

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约