日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

C语言实现简单的线程池【转】

發(fā)布時間:2024/3/13 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C语言实现简单的线程池【转】 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉(zhuǎn)自https://blog.csdn.net/hubi0952/article/details/8045094

線程池的基本原理

在傳統(tǒng)的服務(wù)器結(jié)構(gòu)中,常用一個總的線程監(jiān)聽有沒有新的客戶端連接服務(wù)器。每當(dāng)有一個新的客戶端連接就開啟一個新的線程處理這個客戶端的信息,這個線程只服務(wù)于這個用戶,當(dāng)客戶端和服務(wù)器關(guān)閉連接后服務(wù)器端就銷毀這個線程。

當(dāng)服務(wù)器頻繁的有客戶端連接的時候就要頻繁的開辟與銷毀線程極大的占用了系統(tǒng)的資源。在大量用戶的情況下開辟和銷毀線程將浪費大量的時間和資源。線程池提供了一個解決外部大量用戶與服務(wù)器有限資源的矛盾。線程池和傳統(tǒng)的一個用戶對應(yīng)一個線程的處理方法不同,它的基本思想就是在程序開始時就在內(nèi)存中開辟一些線程,線程的數(shù)目是固定的,它們獨自形成一個類,屏蔽了對外的操作,而服務(wù)器只需要將數(shù)據(jù)包交給線程池就可以了。當(dāng)有新的客戶請求到達時,不是新創(chuàng)建一個線程為其服務(wù),而是從線程池中選擇一個空閑的線程為新的客戶請求服務(wù),服務(wù)完畢后線程進入空閑線程池中。如果沒有線程空閑你的話就將數(shù)據(jù)報暫時積累,等待線程池內(nèi)有線程空閑以后再進行處理。

通過對多個任務(wù)重用已經(jīng)存在的線程對象,降低了對線程對象的創(chuàng)建和銷毀的開銷。當(dāng)客戶請求時,由于線程對象已經(jīng)存在,可以提高請求時間。

線程池的組成部分:

1、線程管理器:用于創(chuàng)建并管理線程池

2、工作線程:線程池中實際執(zhí)行任務(wù)的線程。在初始化線程時會預(yù)先創(chuàng)建好固定數(shù)目的線程在線程池中,這些初始化的線程一般處于空閑狀態(tài)(阻塞(睡眠)),不占用CPU,占用較小的內(nèi)存空間。

3、每個任務(wù)必須實現(xiàn)的接口,當(dāng)線程池中的任務(wù)隊列(任務(wù)鏈表)中有可執(zhí)行任務(wù)時,被空閑的工作線程調(diào)用執(zhí)行(線程的閑與忙是通過互斥量實現(xiàn)的)。把任務(wù)抽象出來形成接口,可做到線程距具體的任務(wù)無關(guān)。

4、任務(wù)隊列:用來存放沒有處理的任務(wù),提供一種緩沖機制,實現(xiàn)這種結(jié)構(gòu)有好幾種方法,常用的有隊列,利用隊列的先進先出原理。

?

什么時候需要線程池?

如果一個應(yīng)用需要頻繁的創(chuàng)建和銷毀線程,而任務(wù)執(zhí)行的時間又非常短,這樣線程創(chuàng)建和銷毀帶來的開銷就不容忽視。這時候就需要線程池了。如果線程創(chuàng)建的和銷毀的時間相比任務(wù)執(zhí)行時間可以忽略不計,則沒有必要使用線程池。

?

?

在Linux系統(tǒng)下用C語言創(chuàng)建一個線程池。線程池會維護一個任務(wù)鏈表(每個CThread_worker結(jié)構(gòu)就是一個任務(wù))。

任務(wù)隊列的結(jié)構(gòu)和線程池的結(jié)構(gòu)

/* *線程池里所有運行和等待的隊列都是一個CThread_worker結(jié)構(gòu) *由于所有的CThread_worker結(jié)構(gòu)都在隊列中,所以是隊列中的一個節(jié)點 */ typedef struct worker {/*回調(diào)函數(shù),當(dāng)任務(wù)運行時會調(diào)用此函數(shù),也可以聲明為其他形式*/void *(*process)(void *arg);/*回調(diào)函數(shù)的參數(shù)*/void *arg; struct worker *next; }CThread_worker;/*線程池的結(jié)構(gòu)*/ typedef struct {pthread_mutex_t queue_lock;pthread_cond_t queue_ready;/*指向任務(wù)等待隊列的隊頭*/CThread_worker *queue_head;/*是否銷毀線程池*/int shutdown;/*線程ID,使用堆空間來分配內(nèi)存*/pthread_t *threadid;/*線程池中線程的數(shù)目*/int max_thread_num;/*當(dāng)前等待隊列的任務(wù)數(shù)目*/int cur_queue_size; }CThread_pool;

?

pool_init()(線程池初始化函數(shù)預(yù)先創(chuàng)建好max_thread_num個線程),每個線程執(zhí)行thread_routine()函數(shù)。

pool_init()實現(xiàn)

void pool_init(int max_thread_num) {pool = (CThread_pool *)malloc(sizeof(CThread_pool));if(pool == NULL){printf("pool_init1\n"); exit(-1);}/*初始化線程互斥鎖和條件變量*/pthread_mutex_init(&(pool->queue_lock), NULL);pthread_cond_init(&(pool->queue_ready), NULL);pool->queue_head = NULL; pool->shutdow = 0;pool->max_thread_num = max_thread_num;pool->threadid = (pthread *)malloc(sizeof(pthread) * max_thread_num);if(pool->threadid == NULL){printf("pool_init2\n"); exit(-1);}/*初始化任務(wù)隊列為0*/ pool->cur_queue_size = 0;int i = 0;/*創(chuàng)建max_thread_num數(shù)目的線程*/for(i-0; i<max_thread_num; i++){pthread_create(&pool->threadid[i], NULL, thread_routine, NULL)} }

thread_routine的實現(xiàn)(每個線程都執(zhí)行的函數(shù))?

void *thread_routine(void *arg) {printf("starting thread 0x%x\n", pthread_self());while(1){/*因為線程中訪問到臨界資源(任意時刻只允許一個線程訪問的資源),所以要上鎖*/pthread_mutex_lock(&pool->queue_lock);/*如果任務(wù)等待隊列為空則線程阻塞,使用了條件變量*/while(pool->cur_queue_size == 0 && !pool->shutdown){/*pthread_cond_wait是一個原子操作,等待前會解鎖,喚醒后會加鎖*/printf("thread 0x%x is waiting\n", pthread_self());pthread_cond_wait(&(pool->queue_ready), &(pool->queue_lock));}if(shutdown){/*遇到break,continue,return等跳轉(zhuǎn)語句,千萬不要忘記先解鎖*/pthread_mutex_unlock(&(pool->queue_lock));printf("thread x%x will exit\n", phread_self());pthread_exit();}printf("thread 0x%x is starting to work\n", pthread_self());/*從任務(wù)隊列中取出任務(wù)*/CThread_worker *worker = pool->queue_head;pool->queue_head = worker->next; pool->cur_queue_size--;/*訪問臨界資源結(jié)束,要解鎖,以便其他線程訪問*/pthread_mutex_unlock(&(pool->queue_lock));/*執(zhí)行任務(wù)等待隊列中的任務(wù)*/worker->process(worker->arg);free(worker);worker = NULL;}pthread_exit(NULL); }

?

?pool_add_worker()函數(shù)向線程池的任務(wù)隊列中加入一個任務(wù),加入后通過調(diào)用pthread_cond_signal (&(pool->queue_ready))喚醒一個處于阻塞狀態(tài)的線程(如果有的話)

void pool_add_worker(void *(*process)(void *arg), void *arg) {//為新的任務(wù)分配內(nèi)存,然后添加到任務(wù)隊列中CThread_woker *newwork = (CThread_worker *)malloc(sizeof(CThread_worker));if(newwork == NULL){printf("pool_add_worker\n");exit(-1);}newwork->process = process;newwork->arg = arg;newwork->next = NULL; /*訪問到臨界資源要上鎖*/pthread_mutex_lock(&(pool->queue_lock));/*將新任務(wù)插入到隊尾*/CThread_worker *temp = pool->queue_head;if(temp == NULL){pool->queue_head = newwork;}else{while(temp->next != NULL){temp = temp->next;}temp->next = newwork;}/*任務(wù)等待隊列的任務(wù)數(shù)加1*/pool->cur_queue_size++;/*解鎖*/pthread_mutex_unlock(&(pool->queue_lock));/*發(fā)信號喚醒任意一個空閑的線程去處理新加入的任務(wù)*/pthread_cond_signal(&(pool->queue_ready));}

?

?pool_destroy ()函數(shù)用于銷毀線程池,線程池任務(wù)鏈表中的任務(wù)不會再被執(zhí)行,但是正在運行的線程會一直把任務(wù)運行完后再退出。

void pool_destory() {if(pool->shutdown){return -1; /*防止兩次調(diào)用*/}pool->shutdown = 1;/*喚醒所有等待的線程*/pthread_cond_broadcast(&(pool->queue_ready));/*阻塞等待線程退出,否則子線程先退出,主線程沒有回收就變成僵尸線程了*/int i;for(i=0; i<pool->max_thread_num; i++){pthread_join(pool->threadid[i], NULL);}free(pool->threadid);pool->threadid = NULL;/*銷毀等待隊列*/CThread_worker *temp;while(pool->queue_head != NULL){temp = pool->queue_head;pool->queue_head = temp->next;free(temp);}/*銷毀條件互斥鎖和條件變量*/pthread_mutex_destory(&(pool->queue_lock));pthread_cond_destory(&(pool->queue_ready));free(pool);/*銷毀后指針置空*/pool = NULL;return 0; }

測試函數(shù)

void *myprocess (void *arg) {printf ("threadid is 0x%x, working on task %d\n", pthread_self (),*(int *) arg);sleep (1);/*休息一秒,延長任務(wù)的執(zhí)行時間*/return NULL; }int main (int argc, char **argv) {pool_init (5);/*線程池中最多三個活動線程*//*連續(xù)向池中投入10個任務(wù)*/int *workingnum = (int *) malloc (sizeof (int) * 10);int i;for (i = 0; i < 10; i++){workingnum[i] = i;pool_add_worker (myprocess, &workingnum[i]);}/*等待所有任務(wù)完成*/sleep (5);/*銷毀線程池*/pool_destroy ();free (workingnum);return 0; }

?

/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/

線程池的目的是為了減少線程創(chuàng)建、銷毀所帶來的代價,當(dāng)有非常多的任務(wù)需要獨立的線程去做的時候,可以使用線程池,從線程池中喚醒一個空閑(睡眠狀態(tài))線程來處理一個個的任務(wù)。

線程池中的每個子線程都是等價的。用線程信號量來控制子線程和任務(wù)的分配問題。設(shè)置一個信號量來表示任務(wù)隊列中的任務(wù)資源,每個子線程都會處于死循環(huán)中,每輪循環(huán)首先等待一個任務(wù)資源信號量,當(dāng)?shù)鹊街?#xff0c;互斥的從任務(wù)隊列中摘取一個任務(wù)節(jié)點。任務(wù)節(jié)點中記錄著該任務(wù)所要執(zhí)行的函數(shù)指針及其參數(shù)。之后子線程開始執(zhí)行該任務(wù)。執(zhí)行完后釋放一個信號量并進去下一輪循環(huán)。當(dāng)信號量小于1(沒有信號量)時,子線程將會阻塞。

因此一個任務(wù)由哪一個線程執(zhí)行,要看那個線程能夠獲取到對應(yīng)的信號量資源。

具體實現(xiàn):

?任務(wù)隊列由雙向鏈表構(gòu)造,每個節(jié)點包含一個任務(wù)的函數(shù)指針和參數(shù)指針。

一般一個簡單的線程池有下列組件:

1、線程池管理器(用于創(chuàng)建并管理線程池)

2、工作線程(線程池中的線程)

3、任務(wù)接口(task,每個任務(wù)必須實現(xiàn)的接口,以供工作線程調(diào)度任務(wù)的執(zhí)行)

4、任務(wù)隊列(用于存放沒有處理的任務(wù)。提供一種緩沖機制)

?

線程池工作的基本邏輯:

1.首先 線程池初始化時 會創(chuàng)建出很多條線程,但他們沒任務(wù)可執(zhí)行時,就會調(diào)用條件變量cond,讓自己沉睡。因此線程池一被創(chuàng)建,就躺著很多條沉睡的線程。

2.線程的執(zhí)行函數(shù)中,有個while(1)循環(huán),讓線程有任務(wù)時執(zhí)行任務(wù),沒任務(wù)時就調(diào)用pthread_cond_wait()來沉睡。

3.當(dāng)有任務(wù)加入線程池的任務(wù)列表時,會通過調(diào)用pthread_cond_signal()來喚醒一條線程(add_task()函數(shù)),然后線程執(zhí)行完就繼續(xù)執(zhí)行下一條任務(wù)或沉睡。

4.當(dāng)線程池中的 shundown變量變成true時,便會調(diào)用pthread_cond_broadcase()喚醒所有沉睡的線程,使線程們自己退出

總結(jié)

以上是生活随笔為你收集整理的C语言实现简单的线程池【转】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。