• 线程池的实现


     

     

     

    概述

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

    半同步/半异步模型: 一个线程(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, 且所有任务都成功完成。这也验证了我这台机器可以同时运行四个线程。



    
    
    
    
    
    
    
    
  • 相关阅读:
    SQL Server系统表sysobjects介绍
    tofixed方法 四舍五入
    (function($){})(jQuery);
    DOS批处理命令-字符串操作
    IF ERRORLEVEL 和 IF %ERRORLEVEL% 区别
    Gpupdate命令详解
    DOS批处理中%cd%和%~dp0的区别
    SetACL 使用方法详细参数中文解析
    Lazarus 1.6 增加了新的窗体编辑器——Sparta_DockedFormEditor.ipk
    Lazarus 1.44升级到1.6 UTF8处理发生变化了
  • 原文地址:https://www.cnblogs.com/persistentsnail/p/3294858.html
Copyright © 2020-2023  润新知