簡單Linux C線程池

http://www.cnblogs.com/venow/archive/2012/11/22/2779667.html

 大多數的網絡服務器,包括Web服務器都具有一個特點,就是單位時間內必須處理數目巨大的連接請求,但是處理時間卻是比較短的。在傳統的多線程服務器模型中是這樣實現的:一旦有個請求到達,就創建一個新的線程,由該線程執行任務,任務執行完畢之后,線程就退出。這就是"即時創建,即時銷毀"的策略。盡管與創建進程相比,創建線程的時間已經大大的縮短,但是如果提交給線程的任務是執行時間較短,而且執行次數非常頻繁,那么服務器就將處于一個不停的創建線程和銷毀線程的狀態。這筆開銷是不可忽略的,尤其是線程執行的時間非常非常短的情況。

  線程池就是為了解決上述問題的,它的實現原理是這樣的:在應用程序啟動之后,就馬上創建一定數量的線程,放入空閑的隊列中。這些線程都是處于阻塞狀態,這些線程只占一點內存,不占用CPU。當任務到來后,線程池將選擇一個空閑的線程,將任務傳入此線程中運行。當所有的線程都處在處理任務的時候,線程池將自動創建一定的數量的新線程,用于處理更多的任務。執行任務完成之后線程并不退出,而是繼續在線程池中等待下一次任務。當大部分線程處于阻塞狀態時,線程池將自動銷毀一部分的線程,回收系統資源。

  下面是一個簡單線程池的實現,這個線程池的代碼是我參考網上的一個例子實現的,由于找不到出處了,就沒辦法注明參考自哪里了。它的方案是這樣的:程序啟動之前,初始化線程池,啟動線程池中的線程,由于還沒有任務到來,線程池中的所有線程都處在阻塞狀態,當一有任務到達就從線程池中取出一個空閑線程處理,如果所有的線程都處于工作狀態,就添加到隊列,進行排隊。如果隊列中的任務個數大于隊列的所能容納的最大數量,那就不能添加任務到隊列中,只能等待隊列不滿才能添加任務到隊列中。

  主要由兩個文件組成一個threadpool.h頭文件和一個threadpool.c源文件組成。源碼中已有重要的注釋,就不加以分析了。

  threadpool.h文件:

復制代碼
struct job
{void* (*callback_function)(void *arg);    //線程回調函數void *arg;                                //回調函數參數struct job *next;
};struct threadpool
{int thread_num;                   //線程池中開啟線程的個數int queue_max_num;                //隊列中最大job的個數struct job *head;                 //指向job的頭指針struct job *tail;                 //指向job的尾指針pthread_t *pthreads;              //線程池中所有線程的pthread_tpthread_mutex_t mutex;            //互斥信號量pthread_cond_t queue_empty;       //隊列為空的條件變量pthread_cond_t queue_not_empty;   //隊列不為空的條件變量pthread_cond_t queue_not_full;    //隊列不為滿的條件變量int queue_cur_num;                //隊列當前的job個數int queue_close;                  //隊列是否已經關閉int pool_close;                   //線程池是否已經關閉
};//================================================================================================
//函數名:                   threadpool_init
//函數描述:                 初始化線程池
//輸入:                    [in] thread_num     線程池開啟的線程個數
//                         [in] queue_max_num  隊列的最大job個數 
//輸出:                    無
//返回:                    成功:線程池地址 失敗:NULL
//================================================================================================
struct threadpool* threadpool_init(int thread_num, int queue_max_num);//================================================================================================
//函數名:                    threadpool_add_job
//函數描述:                  向線程池中添加任務
//輸入:                     [in] pool                  線程池地址
//                          [in] callback_function     回調函數
//                          [in] arg                     回調函數參數
//輸出:                     無
//返回:                     成功:0 失敗:-1
//================================================================================================
int threadpool_add_job(struct threadpool *pool, void* (*callback_function)(void *arg), void *arg);//================================================================================================
//函數名:                    threadpool_destroy
//函數描述:                   銷毀線程池
//輸入:                      [in] pool                  線程池地址
//輸出:                      無
//返回:                      成功:0 失敗:-1
//================================================================================================
int threadpool_destroy(struct threadpool *pool);//================================================================================================
//函數名:                    threadpool_function
//函數描述:                  線程池中線程函數
//輸入:                     [in] arg                  線程池地址
//輸出:                     無  
//返回:                     無
//================================================================================================
void* threadpool_function(void* arg);
復制代碼

  threadpool.c文件:

復制代碼
#include "threadpool.h"struct threadpool* threadpool_init(int thread_num, int queue_max_num)
{struct threadpool *pool = NULL;do {pool = malloc(sizeof(struct threadpool));if (NULL == pool){printf("failed to malloc threadpool!\n");break;}pool->thread_num = thread_num;pool->queue_max_num = queue_max_num;pool->queue_cur_num = 0;pool->head = NULL;pool->tail = NULL;if (pthread_mutex_init(&(pool->mutex), NULL)){printf("failed to init mutex!\n");break;}if (pthread_cond_init(&(pool->queue_empty), NULL)){printf("failed to init queue_empty!\n");break;}if (pthread_cond_init(&(pool->queue_not_empty), NULL)){printf("failed to init queue_not_empty!\n");break;}if (pthread_cond_init(&(pool->queue_not_full), NULL)){printf("failed to init queue_not_full!\n");break;}pool->pthreads = malloc(sizeof(pthread_t) * thread_num);if (NULL == pool->pthreads){printf("failed to malloc pthreads!\n");break;}pool->queue_close = 0;pool->pool_close = 0;int i;for (i = 0; i < pool->thread_num; ++i){pthread_create(&(pool->pthreads[i]), NULL, threadpool_function, (void *)pool);}return pool;    } while (0);return NULL;
}int threadpool_add_job(struct threadpool* pool, void* (*callback_function)(void *arg), void *arg)
{assert(pool != NULL);assert(callback_function != NULL);assert(arg != NULL);pthread_mutex_lock(&(pool->mutex));while ((pool->queue_cur_num == pool->queue_max_num) && !(pool->queue_close || pool->pool_close)){pthread_cond_wait(&(pool->queue_not_full), &(pool->mutex));   //隊列滿的時候就等待
    }if (pool->queue_close || pool->pool_close)    //隊列關閉或者線程池關閉就退出
    {pthread_mutex_unlock(&(pool->mutex));return -1;}struct job *pjob =(struct job*) malloc(sizeof(struct job));if (NULL == pjob){pthread_mutex_unlock(&(pool->mutex));return -1;} pjob->callback_function = callback_function;    pjob->arg = arg;pjob->next = NULL;if (pool->head == NULL)   {pool->head = pool->tail = pjob;pthread_cond_broadcast(&(pool->queue_not_empty));  //隊列空的時候,有任務來時就通知線程池中的線程:隊列非空
    }else{pool->tail->next = pjob;pool->tail = pjob;    }pool->queue_cur_num++;pthread_mutex_unlock(&(pool->mutex));return 0;
}void* threadpool_function(void* arg)
{struct threadpool *pool = (struct threadpool*)arg;struct job *pjob = NULL;while (1)  //死循環
    {pthread_mutex_lock(&(pool->mutex));while ((pool->queue_cur_num == 0) && !pool->pool_close)   //隊列為空時,就等待隊列非空
        {pthread_cond_wait(&(pool->queue_not_empty), &(pool->mutex));}if (pool->pool_close)   //線程池關閉,線程就退出
        {pthread_mutex_unlock(&(pool->mutex));pthread_exit(NULL);}pool->queue_cur_num--;pjob = pool->head;if (pool->queue_cur_num == 0){pool->head = pool->tail = NULL;}else {pool->head = pjob->next;}if (pool->queue_cur_num == 0){pthread_cond_signal(&(pool->queue_empty));        //隊列為空,就可以通知threadpool_destroy函數,銷毀線程函數
        }if (pool->queue_cur_num == pool->queue_max_num - 1){pthread_cond_broadcast(&(pool->queue_not_full));  //隊列非滿,就可以通知threadpool_add_job函數,添加新任務
        }pthread_mutex_unlock(&(pool->mutex));(*(pjob->callback_function))(pjob->arg);   //線程真正要做的工作,回調函數的調用
        free(pjob);pjob = NULL;    }
}
int threadpool_destroy(struct threadpool *pool)
{assert(pool != NULL);pthread_mutex_lock(&(pool->mutex));if (pool->queue_close || pool->pool_close)   //線程池已經退出了,就直接返回
    {pthread_mutex_unlock(&(pool->mutex));return -1;}pool->queue_close = 1;        //置隊列關閉標志while (pool->queue_cur_num != 0){pthread_cond_wait(&(pool->queue_empty), &(pool->mutex));  //等待隊列為空
    }    pool->pool_close = 1;      //置線程池關閉標志pthread_mutex_unlock(&(pool->mutex));pthread_cond_broadcast(&(pool->queue_not_empty));  //喚醒線程池中正在阻塞的線程pthread_cond_broadcast(&(pool->queue_not_full));   //喚醒添加任務的threadpool_add_job函數int i;for (i = 0; i < pool->thread_num; ++i){pthread_join(pool->pthreads[i], NULL);    //等待線程池的所有線程執行完畢
    }pthread_mutex_destroy(&(pool->mutex));          //清理資源pthread_cond_destroy(&(pool->queue_empty));pthread_cond_destroy(&(pool->queue_not_empty));   pthread_cond_destroy(&(pool->queue_not_full));    free(pool->pthreads);struct job *p;while (pool->head != NULL){p = pool->head;pool->head = p->next;free(p);}free(pool);return 0;
}
復制代碼

  測試文件main.c文件:

復制代碼
#include "threadpool.h"void* work(void* arg)
{char *p = (char*) arg;printf("threadpool callback fuction : %s.\n", p);sleep(1);
}int main(void)
{struct threadpool *pool = threadpool_init(10, 20);threadpool_add_job(pool, work, "1");threadpool_add_job(pool, work, "2");threadpool_add_job(pool, work, "3");threadpool_add_job(pool, work, "4");threadpool_add_job(pool, work, "5");threadpool_add_job(pool, work, "6");threadpool_add_job(pool, work, "7");threadpool_add_job(pool, work, "8");threadpool_add_job(pool, work, "9");threadpool_add_job(pool, work, "10");threadpool_add_job(pool, work, "11");threadpool_add_job(pool, work, "12");threadpool_add_job(pool, work, "13");threadpool_add_job(pool, work, "14");threadpool_add_job(pool, work, "15");threadpool_add_job(pool, work, "16");threadpool_add_job(pool, work, "17");threadpool_add_job(pool, work, "18");threadpool_add_job(pool, work, "19");threadpool_add_job(pool, work, "20");threadpool_add_job(pool, work, "21");threadpool_add_job(pool, work, "22");threadpool_add_job(pool, work, "23");threadpool_add_job(pool, work, "24");threadpool_add_job(pool, work, "25");threadpool_add_job(pool, work, "26");threadpool_add_job(pool, work, "27");threadpool_add_job(pool, work, "28");threadpool_add_job(pool, work, "29");threadpool_add_job(pool, work, "30");threadpool_add_job(pool, work, "31");threadpool_add_job(pool, work, "32");threadpool_add_job(pool, work, "33");threadpool_add_job(pool, work, "34");threadpool_add_job(pool, work, "35");threadpool_add_job(pool, work, "36");threadpool_add_job(pool, work, "37");threadpool_add_job(pool, work, "38");threadpool_add_job(pool, work, "39");threadpool_add_job(pool, work, "40");sleep(5);threadpool_destroy(pool);return 0;
}
復制代碼

  用gcc編譯,運行就可以看到效果,1到40個回調函數分別被執行。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383830.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383830.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383830.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++創建對象:棧和堆的區別

首先我們應該了解棧和堆的差別&#xff1a; 詳細信息&#xff1a;傳送門 棧相當于函數自帶的存儲空間&#xff0c;在windows下一般為2M,在Linux下一般為8M&#xff0c;存取速度稍微快一點。堆是系統的空間&#xff0c;相對較大&#xff0c;一般為2G&#xff0c;效率稍微慢一點…

IO多路復用之poll總結

http://www.cnblogs.com/Anker/p/3261006.html 1、基本知識 poll的機制與select類似&#xff0c;與select在本質上沒有多大差別&#xff0c;管理多個描述符也是進行輪詢&#xff0c;根據描述符的狀態進行處理&#xff0c;但是poll沒有最大文件描述符數量的限制。poll和select同…

【C++學習筆記二】C++繼承

繼承 繼承允許我們一句另一個類來定義一個類&#xff0c;這使得繼承和維護一個程序變得更加容易&#xff0c;也達到了重用代碼功能和提高執行效率的效果。 一般格式為&#xff1a; class 派生類名 :訪問修飾符 基類名{};其中訪問修飾符是public protected private中的一個&a…

處理大并發之二 對epoll的理解,epoll客戶端服務端代碼

http://blog.csdn.net/wzjking0929/article/details/51838370 序言&#xff1a; 該博客是一系列的博客&#xff0c;首先從最基礎的epoll說起&#xff0c;然后研究libevent源碼及使用方法&#xff0c;最后研究nginx和node.js&#xff0c;關于select,poll這里不做說明&#xff0c…

C++基類指針指向派生類(指針)

我們常用基類指針指向派生類對象來實現多態性。 私有繼承不允許基類指針指向派生類 基類指針只能訪問到基類中含有的公有成員。 當用基類指針指向派生類對象在動態分配堆上內存的時候&#xff0c;析構函數必須是虛函數! 成員如果是數據成員的話訪問的是基類的版本&#xff…

C++虛繼承中構造函數和析構函數順序問題以及原理

多重繼承的問題&#xff1a;多個類B,C,…繼承同一個類A導致如果X繼承了B,C,…那么在X中將還有多個A中成員的拷貝&#xff0c;如果想要訪問A中的成員如果不加名字空間將會導致二義性&#xff0c;這種拷貝大多是沒有實際意義的&#xff0c;為了避免這種空間浪費&#xff0c;C有虛…

一個簡單的linux線程池

http://blog.csdn.net/wzjking0929/article/details/20312675 線程池&#xff1a;簡單地說&#xff0c;線程池 就是預先創建好一批線程&#xff0c;方便、快速地處理收到的業務。比起傳統的到來一個任務&#xff0c;即時創建一個線程來處理&#xff0c;節省了線程的創建和回收的…

【C++學習筆記三】C++多態、抽象(接口)

當類之間存在多種層次結構&#xff0c;并且類之間通過繼承關聯時就會用到多態。 虛函數在子類中的覆蓋版本和該函數在基類中的原始版本必須有相同的函數簽名、函數名、形參名、常屬性。如果返回值為非類類型&#xff0c;則必須相同&#xff0c;如果是類類型A的指針或者引用&am…

C++重載和重寫的條件以及重寫后對基類函數的覆蓋

重載&#xff1a;同一個類中名字相同&#xff0c;參數列表不同的方法構成重載函數&#xff0c;和返回值沒有關系。這就意味著就算返回值不同&#xff0c;只要名字相同參數列表相同編譯器還是會報錯&#xff0c;覺得一函數被定義了兩次。 重寫&#xff1a;派生類中只要函數名字…

C++靜態成員和靜態方法

在類中&#xff0c;靜態成員可以實現多個對象之間共享數據&#xff0c;同時保證了安全性。靜態數據對該類的所有對象是公有的&#xff0c;存儲一處供所有對象使用。 注意&#xff1a; 靜態成員定義時需要在前面加上關鍵字static靜態成員必須初始化且必須在類外進行&#xff0…

基于epoll的簡單的http服務器

http://blog.csdn.net/fangjian1204/article/details/34415651 http服務器已經可以處理并發連接&#xff0c;支持多個客戶端并發訪問&#xff0c;每個連接可以持續讀寫數據&#xff0c;當然&#xff0c;這只是一個簡單的學習例子&#xff0c;還有很多bug&#xff0c;發表出來只…

C++單例模式簡單實現

有時候我們需要某個類只能被實例化一次&#xff0c;并且其他類都可以訪問到這個類&#xff0c;就需要這種設計模式。 例如我們想要做個資源管理器&#xff0c;顯然這個管理器只能有一個。 這種模式有很多實現方式&#xff0c;這里介紹最簡單的一種&#xff0c;想要了解更多可…

Linux C++ 實現線程池

http://blog.csdn.net/qq_25425023/article/details/53914609 線程池中的線程&#xff0c;在任務隊列為空的時候&#xff0c;等待任務的到來&#xff0c;任務隊列中有任務時&#xff0c;則依次獲取任務來執行&#xff0c;任務隊列需要同步。 Linux線程同步有多種方法&#xff…

C++制表符

制表符的轉義字符為\t&#xff0c;一般情況下長度為8個空格&#xff0c;這里的8個指的是從上一個字符串的開頭開始算&#xff0c;往后數8個&#xff0c;不夠的話就補空格。 如果前面的字符串的長度大于等于8個&#xff0c;例如前面字符串的長度為x,那么就會補(8-x%8)個空格 例…

C++派生類含有成員對象構造函數析構函數順序

參考博客&#xff1a;傳送門1 當類中含有對象成員時&#xff1a; 類的構造函數要包含對成員對象的初始化&#xff0c;如果構造函數的成員初始化列表沒有包含對成員對象的初始化&#xff0c;系統會自動調用成員對象的無參構造函數。順序上&#xff1a;先調用成員對象的構造函數…

c,c++中字符串處理函數strtok,strstr,strchr,strsub

http://blog.csdn.net/wangqing_12345/article/details/51760220 1&#xff0c;字符串切割函數 函數原型&#xff1a;char *strtok(char *s, char *delim); 函數功能&#xff1a;把字符串s按照字符串delim進行分割&#xff0c;然后返回分割的結果。 函數使用說&#xff1a; 1…

C++虛基類成員可見性

詳見《CPrimer》[第五版]719頁 如果繼承路徑上沒有和虛基類成員重名的成員&#xff0c;則不存在二義性&#xff0c;因為我們僅能訪問到虛基類成員。 當訪問僅有一條繼承路徑上含有和虛基類成員重名的成員&#xff0c;也不存在二義性。派生類的成員的優先級比基類的成員高&…

鏈表逆序的原理及實例

http://blog.csdn.net/wangqing_12345/article/details/51757294 尾插法建立鏈表&#xff0c;帶頭結點設鏈表節點為typedef struct node {int data;struct node *next;}node_t, *pnode_t;要求將一帶鏈表頭List head的單向鏈表逆序。 分析&#xff1a; 1). 若鏈表為空或只有一個…

C++關于虛基類、構造函數、析構函數、成員對象的兩個程序淺析

預備博客&#xff1a; C虛繼承中構造函數和析構函數順序問題以及原理 C派生類含有成員對象構造函數析構函數順序 C虛基類成員可見性 程序一如下&#xff1a; #include<iostream> using namespace std; class A { public:A(int a) :x(a) { cout << "A const…

strtok函數及其實現

頭文件&#xff1a;#include <string.h> 定義函數&#xff1a;char * strtok(char *s, const char *delim); 函數說明&#xff1a;strtok()用來將字符串分割成一個個片段。參數s 指向欲分割的字符串&#xff0c;參數delim 則為分割字符串&#xff0c;當 strtok()在參數s …