线程池的实现

 

 

 

概述

线程池主要有两种实现模型:

半同步/半异步模型: 一个线程(ManagerThread)在工作队列上侦听,一旦主线程往工作队列push新的工作任务,ManagerThread拿出这个任务分派给某个空闲的工作线程去执行。在这个过程中ManagerThread负责任务调度,真正做事的是WorkerThread。异步指对任务请求是异步的,同步指分派处理任务过程中需要做同步操作。

领导者/跟随者模型:有一个线程起初是leader,其他的线程是followers,当有任务请求到达时,leader接下这个任务处理,同时从followers中选择一个新的leader ,这个过程中始终只有一个leader线程在侦听任务。

两者各有优缺点,这里选择半同步/半异步模型来实现这个线程池。

实现

A.   Manager Thread

半同步/半异步模型的核心是ManagerThreadManagerThread工作流程图是:

在这个过程中需要注意的是两个队列:


1.工作任务队列,数据结构定义为:

typedef struct _task_list

{

task_node         *head;

task_node         *tail;

pthread_mutex_t   *mutex;

pthread_cond_t    *cond;

}task_list;

主线程即调用线程往任务队列末尾添加新任务,ManagerThread从队首拿任务去派发,这个过程遵守FIFO原则。因为不同线程操作这个队列所以需要同步。

2.空闲工作线程队列,数据结构定义为:

typedef struct _idle_thread_id_array

{

        int  *idxs;

        int  size;

        pthread_mutex_t  *mutex;

        pthread_cond_t   *cond;

}idle_thread_array;

这个队列主要是在Manager Thread和Worker Threads,Worker Threads互相之间同步,Worker Threads做完了Manager Thread派发的任务后把自己置入idle array中,Manager Thread每次从idle array中取一个idle Worker Thread去做任务,如果idle array中没有空闲线程,且池子里的线程数没有超过池子的大小,就会去新建一个Worker Thread,如果超过了,Manager Thread就会在idle array上等待,直到有新的Worker Thread变为空闲。

空闲工作线程队列数据结构中,其实是一个int数组,保存threads数组的索引,threads数组是一个所有Worker Threads构成的数组,数据结构是

typedef struct _thread_array

{

        thread_info  *threads;

        int          size;

        int            *unused;

        int            unused_size;

}thread_array;

thread_info是线程信息结构体,thread_array包括thread_info数组,和一个"unused int"数组,这个数组是因为线程池有自动回收策略,当一个WorkerThread空闲了一定时间而没有被分派新任务时,会被回收以节省系统资源,相当于从数组中删去某一项,这时会在数组中出现很多‘洞’,unused保存这些‘洞’索引,当新 的Worker Thread被添加进池子时,从unused选一个位置去放置新线程的信息。

 

B.    Worker Threader

    WorkerThread的工作流程图是:

     

具体细节实现主要是注意尽量减小同步代码的颗粒度。

 

 

测试

    测试代码可以这样写,让线程池做大量任务,任务执行过程是将该任务相关变量置1(初始化时是0),程序退出时,检查任务是否都完成,打印完成任务所花的时间。作为比对,同时写一个单线程测试案例完成上述过程,最后比较两者所花的时间。

   我的机器是双核四线程,在没有其他很占cpu的进程开启下,使用线程池跑的案例所耗费的时间是没使用的1/4, 且所有任务都成功完成。这也验证了我这台机器可以同时运行四个线程。







posted @ 2012-05-14 11:33  persistentsnail  阅读(271)  评论(0编辑  收藏  举报