Linux下線程池的理解與簡單實現


  首先,線程池是什么?顧名思義,就是把一堆開辟好的線程放在一個池子里統一管理,就是一個線程池。

  其次,為什么要用線程池,難道來一個請求給它申請一個線程,請求處理完了釋放線程不行么?也行,但是如果創建線程和銷毀線程的時間比線程處理請求的時間長,而且請求很多的情況下,我們的CPU資源都浪費在了創建和銷毀線程上了,所以這種方法的效率比較低,於是,我們可以將若干已經創建完成的線程放在一起統一管理,如果來了一個請求,我們從線程池中取出一個線程來處理,處理完了放回池內等待下一個任務,線程池的好處是避免了繁瑣的創建和結束線程的時間,有效的利用了CPU資源。

  按照我的理解,線程池的作用和雙緩沖的作用類似,可以完成任務處理的“魚貫”動作。

  最后,如何才能創建一個線程池的模型呢,一般需要以下三個參與者:

    1、線程池結構,它負責管理多個線程並提供任務隊列的接口

    2、工作線程,它們負責處理任務

    3、任務隊列,存放待處理的任務

  有了三個參與者,下一個問題就是怎么使線程池安全有序的工作,可以使用POSIX中的信號量、互斥鎖和條件變量等同步手段。有了這些認識,我們就可以創建自己的線程池模型,我在github上找了一個比較經典的線程池的例子,有興趣的可以學習一下。

  原作者github地址:https://github.com/Pithikos/C-Thread-Pool

   線程池所需要的數據結構:

    (1)、0/1信號量,用於當任務隊列非空時通知線程,這里是用互斥鎖和條件變量來實現的信號量,其實POSIX信號量的一種實現就是用的互斥鎖和條件變量

/* Binary semaphore */
typedef struct bsem {
    pthread_mutex_t mutex;
    pthread_cond_t   cond;
    int v;   //v的值非0即1
} bsem;

    (2)、標識任務的結構體,prev指向的對象是當前任務的前一個任務,這里用pnext來標識更貼切

/* Job */
typedef struct job{
    struct job*  prev;                   /* pointer to previous job   */
    void*  (*function)(void* arg);       /* function pointer          */
    void*  arg;                          /* function's argument       */
} job;

    (3)、工作隊列

/* Job queue */
typedef struct jobqueue{
    pthread_mutex_t rwmutex;             /* used for queue r/w access */
    job  *front;                         /* pointer to front of queue */
    job  *rear;                          /* pointer to rear  of queue */
    bsem *has_jobs;                      /* flag as binary semaphore  */
    int   len;                           /* number of jobs in queue   */
} jobqueue;

    互斥鎖rwmutex用來同步對工作隊列的讀寫操作,front用來標識工作隊列中的第一個任務,rear用來標識工作隊列中的最后一個任務,has_jobs用來提供對二值信號量的訪問接口,len代表當前工作隊列中的任務數量。

    (4)、工作線程

/* Thread */
typedef struct thread{
    int       id;                        /* friendly id               */
    pthread_t pthread;                   /* pointer to actual thread  */
    struct thpool_* thpool_p;            /* access to thpool          */
} thread;

     id標識第幾個線程,pthread代表的是創建的真正的線程id,對於每個線程來說,都提供對所在線程池的訪問

    (5)、線程池結構

/* Threadpool */
typedef struct thpool_{
    thread**   threads;                  /* pointer to threads        */
    volatile int num_threads_alive;      /* threads currently alive   */
    volatile int num_threads_working;    /* threads currently working */
    pthread_mutex_t  thcount_lock;       /* used for thread count etc */
    jobqueue*  jobqueue_p;               /* pointer to the job queue  */    
} thpool_;

     threads可以看做是一個指針數組,數組中的每個指針都指向一個線程結構,num_threads_alive標識的是線程池中有多少個可工作線程,num_threads_working代表的是當前線程池中正在工作的線程數目,互斥鎖thcount_lock提供對線程池數據的互斥訪問,同時,線程池需要和任務隊列協作,所以還要提供對任務隊列的訪問。

  線程池的工作流程:

    初始化線程池、任務隊列和工作線程->向任務隊列中添加任務->將等候在條件變量(任務隊列上有任務)上的一個線程喚醒並從該任務隊列中取出第一個任務給該線程執行->等待任務隊列中所有任務執行完畢->關閉線程池。

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM