物聯網學習教程——執行緒池

千鋒教育官方發表於2019-09-23


執行緒池

執行緒池基本原理

在傳統伺服器結構中,常是有一個總的監聽執行緒監聽有沒有新的使用者連線伺服器,每當有一個新的使用者進入,伺服器就開啟一個新的執行緒使用者處理這 個使用者的資料包。這個執行緒只服務於這個使用者,當使用者與伺服器端關閉連線以後,伺服器端銷燬這個執行緒。

然而頻繁地開闢與銷燬執行緒極大地佔用了系統的資源,而且在大量使用者的情況下,系統為了開闢和銷燬執行緒將浪費大量的時間和資源。執行緒池提供了一個解決外部大量使用者與伺服器有限資源的矛盾。

執行緒池和傳統的一個使用者對應一個執行緒的處理方法不同,它的基本思想就是在程式開始時就在記憶體中開闢一些執行緒,執行緒的數目是固定的,他們獨自形成一個類,遮蔽了對外的操作,而伺服器只需要將資料包交給執行緒池就可以了。當有新的客戶請求到達時,不是新建立一個執行緒為其服務,而是從“池子”中選擇一個空閒的執行緒為新的客戶請求服務,服務完畢後,執行緒進入空閒執行緒池中。如果沒有執行緒空閒的話,就將資料包暫時積累, 等待執行緒池內有執行緒空閒以後再進行處理。透過對多個任務重用已經存在的執行緒物件,降低了對執行緒物件建立和銷燬的開銷。當客戶請求 時,執行緒物件已經存在,可以提高請求的響應時間,從而整體地提高了系統服務的表現。

執行緒池應用例項

一般來說實現一個執行緒池主要包括以下幾個組成部分:

1)執行緒管理器:用於建立並管理執行緒池。

2)工作執行緒:執行緒池中實際執行任務的執行緒。在初始化執行緒時會預先建立好固定數目的執行緒在池中,這些初始化的執行緒一般處於空閒狀態,一般不佔用 CPU,佔用較小的記憶體空間。

3)任務介面:每個任務必須實現的介面,當執行緒池的任務佇列中有可執行任務時,被空閒的工作執行緒調去執行(執行緒的閒與忙是透過互斥量實現的),把任務抽象出來形成介面,可以做到執行緒池與具體的任務無關。

4)任務佇列:用來存放沒有處理的任務,提供一種緩衝機制,實現這種結構有好幾種方法,常用的是佇列,主要運用先進先出原理,另外一種是連結串列之類的資料結構,可以動態的為它分配記憶體空間,應用中比較靈活,此教程就是用到的連結串列。

什麼時候需要建立執行緒池呢?

簡單的說,如果一個應用需要頻繁的建立和銷燬執行緒,而任務執行的時間又非常短,這樣執行緒建立和銷燬的帶來的開銷就不容忽視,這時也是執行緒池該出場的機會了。如果執行緒建立和銷燬時間相比任務執行時間可以忽略不計,則沒有必要使用執行緒池了

 

執行緒池實現示例程式碼如下:

thread_pool.h 的示例程式碼:

#ifndef __THREAD_POOL_H__  

#define __THREAD_POOL_H__  

#include <pthread.h>    

 /*********************************************************************

* 任務回撥函式,也可根據需要自行修改

*********************************************************************/  

typedef void *(*pool_task_f)(void *arg);    

/*********************************************************************

* 任務控制程式碼

*********************************************************************/  

typedef struct _task{  

    pool_task_f process;/*回撥函式,任務執行時會呼叫此函式,注意也可宣告成其它形式*/  

    void *arg;     /*回撥函式的引數*/  

    struct _task *next;  

}pool_task;    

/*********************************************************************

* 執行緒池控制程式碼

*********************************************************************/  

typedef struct  

{  

    pthread_t *threadid;        /* 執行緒號 */  

    int threads_limit;          /* 執行緒池中允許的活動執行緒數目 */  

    int destroy_flag;           /* 是否銷燬執行緒池 , 0銷燬,1不銷燬*/  

    pool_task *queue_head;      /* 連結串列結構,執行緒池中所有等待任務 */  

    int task_in_queue;          /* 當前等待佇列的任務數目 */  

    pthread_mutex_t queue_lock; /* 鎖 */  

    pthread_cond_t queue_ready; /* 條件變數 */  

}pool_t;    

/*********************************************************************

*功能:        初始化執行緒池結構體並建立執行緒

*引數:         

            pool:執行緒池控制程式碼

            threads_limit:執行緒池中執行緒的數量

*返回值:   無

*********************************************************************/  

void pool_init(pool_t *pool, int threads_limit);    

/*********************************************************************

*功能:        銷燬執行緒池,等待佇列中的任務不會再被執行,

             但是正在執行的執行緒會一直,把任務執行完後再退出

*引數:        執行緒池控制程式碼

*返回值:   成功:0,失敗非0

*********************************************************************/  

int pool_uninit(pool_t *pool);    

/*********************************************************************

*功能:        向執行緒池中新增一個任務

*引數:         

            pool:執行緒池控制程式碼

            process:任務處理函式

            arg:任務引數

*返回值:   0

*********************************************************************/  

int pool_add_task(pool_t *pool, pool_task_f process, void *arg);  

  

  

#endif  

thread_pool.c 的示例程式碼:

#include <stdio.h>  

#include <stdlib.h>  

#include <pthread.h>  

#include <assert.h>  

  

#include "thread_pool.h"    

static void *pool_thread_server(void *arg);   

/*********************************************************************

*功能:        初始化執行緒池結構體並建立執行緒

*引數:         

            pool:執行緒池控制程式碼

            threads_limit:執行緒池中執行緒的數量

*返回值:   無

*********************************************************************/  

void pool_init(pool_t *pool, int threads_limit)  

{  

    pool->threads_limit = threads_limit;  

    pool->queue_head = NULL;  

    pool->task_in_queue = 0;  

    pool->destroy_flag = 0;  

    /*建立存放執行緒ID的空間*/  

    pool->threadid = (pthread_t *)calloc(threads_limit, sizeof(pthread_t));  

    int i = 0;  

    /*初始化互斥鎖和條件變數*/  

    pthread_mutex_init(&(pool->queue_lock), NULL);  

    pthread_cond_init(&(pool->queue_ready), NULL);  

    /*迴圈建立threads_limit個執行緒*/  

    for (i = 0; i < threads_limit; i++){  

        pthread_create(&(pool->threadid[i]), NULL, pool_thread_server, pool);  

    }  

    return;  

}    

/*********************************************************************

*功能:        銷燬執行緒池,等待佇列中的任務不會再被執行,

             但是正在執行的執行緒會一直,把任務執行完後再退出

*引數:        執行緒池控制程式碼

*返回值:   成功:0,失敗非0

*********************************************************************/  

int pool_uninit(pool_t *pool)  

{  

    pool_task *head = NULL;  

    int i;        

    pthread_mutex_lock(&(pool->queue_lock));  

    if(pool->destroy_flag)/* 防止兩次呼叫 */  

        return -1;  

    pool->destroy_flag = 1;  

    pthread_mutex_unlock(&(pool->queue_lock));  

    /* 喚醒所有等待執行緒,執行緒池要銷燬了 */  

    pthread_cond_broadcast(&(pool->queue_ready));  

    /* 阻塞等待執行緒退出,否則就成殭屍了 */  

    for (i = 0; i < pool->threads_limit; i++)  

        pthread_join(pool->threadid[i], NULL);  

    free(pool->threadid);  

    /* 銷燬等待佇列 */  

    pthread_mutex_lock(&(pool->queue_lock));  

    while(pool->queue_head != NULL){  

        head = pool->queue_head;  

        pool->queue_head = pool->queue_head->next;  

        free(head);  

    }  

    pthread_mutex_unlock(&(pool->queue_lock));  

    /*條件變數和互斥量也別忘了銷燬*/  

    pthread_mutex_destroy(&(pool->queue_lock));  

    pthread_cond_destroy(&(pool->queue_ready));  

    return 0;  

}    

/*********************************************************************

*功能:        向任務佇列中新增一個任務

*引數:        pool:執行緒池控制程式碼

               process:任務處理函式

               arg:任務引數

*返回值:   無

*********************************************************************/  

static void enqueue_task(pool_t *pool, pool_task_f process, void *arg)  

{  

    pool_task *task = NULL;  

    pool_task *member = NULL;        

    pthread_mutex_lock(&(pool->queue_lock));        

    if(pool->task_in_queue >= pool->threads_limit){  

        printf("task_in_queue > threads_limit!\n");  

        pthread_mutex_unlock (&(pool->queue_lock));  

        return;  

    }        

    task = (pool_task *)calloc(1, sizeof(pool_task));  

    assert(task != NULL);  

    task->process = process;  

    task->arg = arg;  

    task->next = NULL;  

    pool->task_in_queue++;  

    member = pool->queue_head;  

    if(member != NULL){  

        while(member->next != NULL)  /* 將任務加入到任務鏈連的最後位置. */  

            member = member->next;  

        member->next = task;  

    }else{  

        pool->queue_head = task; /* 如果是第一個任務的話,就指向頭 */  

    }  

    printf("\ttasks %d\n", pool->task_in_queue);  

    /* 等待佇列中有任務了,喚醒一個等待執行緒 */  

    pthread_cond_signal (&(pool->queue_ready));  

    pthread_mutex_unlock (&(pool->queue_lock));  

}    

/*********************************************************************

*功能:        從任務佇列中取出一個任務

*引數:        執行緒池控制程式碼

*返回值:   任務控制程式碼

*********************************************************************/  

static pool_task *dequeue_task(pool_t *pool)  

{  

    pool_task *task = NULL;        

    pthread_mutex_lock(&(pool->queue_lock));  

    /* 判斷執行緒池是否要銷燬了 */  

    if(pool->destroy_flag){  

        pthread_mutex_unlock(&(pool->queue_lock));  

        printf("thread 0x%lx will be destroyed\n", pthread_self());  

        pthread_exit(NULL);  

    }  

    /* 如果等待佇列為0並且不銷燬執行緒池,則處於阻塞狀態 */  

    if(pool->task_in_queue == 0){  

        while((pool->task_in_queue == 0) && (!pool->destroy_flag)){  

            printf("thread 0x%lx is waitting\n", pthread_self());  

            /* 注意:pthread_cond_wait是一個原子操作,等待前會解鎖,喚醒後會加鎖 */  

            pthread_cond_wait(&(pool->queue_ready), &(pool->queue_lock));  

        }  

    }else{  

        /* 等待佇列長度減去1,並取出佇列中的第一個元素 */  

        pool->task_in_queue--;  

        task = pool->queue_head;  

        pool->queue_head = task->next;  

        printf("thread 0x%lx received a task\n", pthread_self());  

    }  

    pthread_mutex_unlock(&(pool->queue_lock));  

    return task;  

}    

/*********************************************************************

*功能:        向執行緒池中新增一個任務

*引數:        pool:執行緒池控制程式碼

               process:任務處理函式

               arg:任務引數

*返回值:   0

*********************************************************************/  

int pool_add_task(pool_t *pool, pool_task_f process, void *arg)  

{  

    enqueue_task(pool, process, arg);  

    return 0;  

}    

/*********************************************************************

*功能:        執行緒池服務程式

*引數:        略

*返回值:   略

*********************************************************************/  

static void *pool_thread_server(void *arg)  

{  

    pool_t *pool = NULL;        

    pool = (pool_t *)arg;  

    while(1){  

        pool_task *task = NULL;  

        task = dequeue_task(pool);  

        /*呼叫回撥函式,執行任務*/  

        if(task != NULL){  

            printf ("thread 0x%lx is busy\n", pthread_self());  

            task->process(task->arg);  

            free(task);  

            task = NULL;  

        }  

    }  

    /*這一句應該是不可達的*/  

    pthread_exit(NULL);    

    return NULL;  

}  

 

下面是測試程式碼:

#include <stdio.h>  

#include <unistd.h>   

#include "thread_pool.h"    

void *task_test(void *arg)  

{  

    printf("\t\tworking on task %d\n", (int)arg);  

    sleep(1);           /*休息一秒,延長任務的執行時間*/  

    return NULL;  

}    

void thread_pool_demo(void)  

{  

    pool_t pool;  

    int i = 0;    

    pool_init(&pool, 2);//初始化一個執行緒池,其中建立2個執行緒  

    sleep(1);  

    for(i = 0; i < 5; i++){  

        sleep(1);  

        pool_add_task(&pool, task_test, (void *)i);//新增一個任務  

    }  

    sleep(4);   

    pool_uninit(&pool);//刪除執行緒池  

}    

int main (int argc, char *argv[])  

{    

    thread_pool_demo();  

    return 0;  

}  

執行結果如下:

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69914734/viewspace-2657963/,如需轉載,請註明出處,否則將追究法律責任。

相關文章