Linux进程池、线程池调研
1.進(jìn)程池
(1)進(jìn)程池是由服務(wù)器預(yù)先創(chuàng)建的一組子進(jìn)程,這些子進(jìn)程的數(shù)目在3-10個(gè)之間。進(jìn)程池中的所有子進(jìn)程都運(yùn)行著相同的代碼,并具有相同的屬性,比如優(yōu)先級(jí),PGID等等。因?yàn)檫M(jìn)程池在服務(wù)器啟動(dòng)之初就創(chuàng)建好了,所以他們沒有打開不必要的文件描述符(從父進(jìn)程繼承而來),也不會(huì)錯(cuò)誤地使用大塊的堆內(nèi)存(從父進(jìn)程復(fù)制得到)。
(2) 當(dāng)有新的任務(wù)到來時(shí),主進(jìn)程將通過某種方式選擇進(jìn)程池中的某一個(gè)子進(jìn)程來為之服務(wù)。相對(duì)于動(dòng)態(tài)創(chuàng)建子進(jìn)程,選擇一個(gè)已經(jīng)存在的子進(jìn)程的代價(jià)明顯要小的多。至于主進(jìn)程選擇哪個(gè)進(jìn)程來為新任務(wù)服務(wù),則有兩種方式:
①主進(jìn)程使用某種算法主動(dòng)選擇子進(jìn)程。最簡(jiǎn)單、最常用的算法是隨機(jī)算法和Round-Robin(輪流選取)算法,但更優(yōu)秀、更智能的算法將使任務(wù)在各個(gè)工作進(jìn)程中更均勻地分配、從而減輕服務(wù)器的整體壓力。
②主進(jìn)程和所有子進(jìn)程通過一個(gè)共享的工作隊(duì)列來同步,子進(jìn)程都睡眠在該工作隊(duì)列上。當(dāng)有心的任務(wù)到來時(shí),主進(jìn)程將任務(wù)添加到工作隊(duì)列中。這將喚醒正在等待任務(wù)的子進(jìn)程,不過只有一個(gè)子進(jìn)程獲得新任務(wù)的“接管權(quán)”,它可以從工作隊(duì)列中取出并執(zhí)行之,而其他子進(jìn)程將繼續(xù)睡眠在工作隊(duì)列上。
當(dāng)選擇好子進(jìn)程后,主進(jìn)程還需要使用某種通知機(jī)制來告訴目標(biāo)子進(jìn)程有新進(jìn)程需要處理,并傳遞必要的數(shù)據(jù):
在父進(jìn)程和子進(jìn)程之間預(yù)先建立好一條管道,然后通過該管道來實(shí)現(xiàn)所有進(jìn)程間通信。在父線程和子線程之間傳遞數(shù)據(jù)就要簡(jiǎn)單的多,因?yàn)槲覀兛梢园堰@些數(shù)據(jù)定義全局的,那么他們本身就是被所有線程共享的。
2.線程池
(1)基本功能
線程池提供了一個(gè)解決外部大量用戶與服務(wù)器有限資源的矛盾,它的基本思想就是在程序 開始時(shí)就在內(nèi)存中開辟一些線程, 線程的數(shù)目是 固定的,他們獨(dú)自形成一個(gè)類, 屏蔽了對(duì)外的操作, 而服務(wù)器只需要將數(shù)據(jù)包交給線程池就可以了。
當(dāng)有新的客戶請(qǐng)求到達(dá)時(shí) , 不是新創(chuàng)建一個(gè)線程為其服務(wù) , 而是從“池子”中選擇一個(gè)空閑的線程為新的客戶請(qǐng)求服務(wù) ,服務(wù)完畢后 , 線程進(jìn)入空閑線程池中。如果沒有線程空閑的話, 就將數(shù)據(jù)包暫時(shí)積累 ,等待線程池內(nèi)有線程空閑以后再進(jìn)行處理。通過對(duì)多個(gè)任務(wù)重用已經(jīng)存在的線程對(duì)象 , 降低了對(duì)線程對(duì)象創(chuàng)建和銷毀的開銷。當(dāng)客戶請(qǐng)求時(shí) , 線程對(duì)象已經(jīng)存在, 可以提高請(qǐng)求的響應(yīng)時(shí)間 , 從而整體地提高了系統(tǒng)服務(wù)的表現(xiàn)。
(2)一個(gè)線程池主要包括以下幾個(gè)組成部分:
①線程管理器:用于創(chuàng)建并管理線程池
②工作線程:線程池中實(shí)際執(zhí)行任務(wù)的線程。在初始化線程時(shí)會(huì)預(yù)先創(chuàng)建好固定數(shù)目的線程在池中,這些初始化的線程一般處于空閑狀態(tài),一般不占用CPU,占用較小的內(nèi)存空間。
③任務(wù)接口:每個(gè)任務(wù)必須實(shí)現(xiàn)的接口,當(dāng)線程池的任務(wù)隊(duì)列中有可執(zhí)行任務(wù)時(shí),被空閑的工作線程調(diào)去執(zhí)行(線程的閑與忙是通過互斥量實(shí)現(xiàn)的,跟前面文章中的設(shè)置標(biāo)志位差不多),把任務(wù)抽象出來形成接口,可以做到線程池與具體的任務(wù)無關(guān)。
④任務(wù)隊(duì)列:用來存放沒有處理的任務(wù),提供一種緩沖機(jī)制,實(shí)現(xiàn)這種結(jié)構(gòu)有好幾種方法,常用的是隊(duì)列,主要運(yùn)用先進(jìn)先出原理,另外一種是鏈表之類的數(shù)據(jù)結(jié)構(gòu),可以動(dòng)態(tài)的為它分配內(nèi)存空間,應(yīng)用中比較靈活,下文中就是用到的鏈表。
3.實(shí)現(xiàn)一個(gè)線程池:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <pthread.h> #include <assert.h> /* *線程池里所有運(yùn)行和等待的任務(wù)都是一個(gè)CThread_worker *由于所有任務(wù)都在鏈表里,所以是一個(gè)鏈表結(jié)構(gòu) */ typedef struct worker { /*回調(diào)函數(shù),任務(wù)運(yùn)行時(shí)會(huì)調(diào)用此函數(shù),注意也可聲明成其它形式*/ void *(*process) (void *arg); void *arg;/*回調(diào)函數(shù)的參數(shù)*/ struct worker *next; } CThread_worker; /*線程池結(jié)構(gòu)*/ typedef struct { pthread_mutex_t queue_lock; pthread_cond_t queue_ready; /*鏈表結(jié)構(gòu),線程池中所有等待任務(wù)*/ CThread_worker *queue_head; /*是否銷毀線程池*/ int shutdown; pthread_t *threadid; /*線程池中允許的活動(dòng)線程數(shù)目*/ int max_thread_num; /*當(dāng)前等待隊(duì)列的任務(wù)數(shù)目*/ int cur_queue_size; } CThread_pool; int pool_add_worker (void *(*process) (void *arg), void *arg); void *thread_routine (void *arg); //share resource static CThread_pool *pool = NULL; void pool_init (int max_thread_num) { pool = (CThread_pool *) malloc (sizeof (CThread_pool)); pthread_mutex_init (&(pool->queue_lock), NULL); pthread_cond_init (&(pool->queue_ready), NULL); pool->queue_head = NULL; pool->max_thread_num = max_thread_num; pool->cur_queue_size = 0; pool->shutdown = 0; pool->threadid = (pthread_t *) malloc (max_thread_num * sizeof (pthread_t)); int i = 0; for (i = 0; i < max_thread_num; i++) { pthread_create (&(pool->threadid[i]), NULL, thread_routine,NULL); } } /*向線程池中加入任務(wù)*/ int pool_add_worker (void *(*process) (void *arg), void *arg) { /*構(gòu)造一個(gè)新任務(wù)*/ CThread_worker *newworker = (CThread_worker *) malloc (sizeof (CThread_worker)); newworker->process = process; newworker->arg = arg; newworker->next = NULL;/*別忘置空*/ pthread_mutex_lock (&(pool->queue_lock)); /*將任務(wù)加入到等待隊(duì)列中*/ CThread_worker *member = pool->queue_head; if (member != NULL) { while (member->next != NULL) member = member->next; member->next = newworker; } else { pool->queue_head = newworker; } assert (pool->queue_head != NULL); pool->cur_queue_size++; pthread_mutex_unlock (&(pool->queue_lock)); /*好了,等待隊(duì)列中有任務(wù)了,喚醒一個(gè)等待線程; 注意如果所有線程都在忙碌,這句沒有任何作用*/ pthread_cond_signal (&(pool->queue_ready)); return 0; } /*銷毀線程池,等待隊(duì)列中的任務(wù)不會(huì)再被執(zhí)行,但是正在運(yùn)行的線程會(huì)一直 把任務(wù)運(yùn)行完后再退出*/ int pool_destroy () { 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); /*銷毀等待隊(duì)列*/ CThread_worker *head = NULL; while (pool->queue_head != NULL) { head = pool->queue_head; pool->queue_head = pool->queue_head->next; free (head); } /*條件變量和互斥量也別忘了銷毀*/ pthread_mutex_destroy(&(pool->queue_lock)); pthread_cond_destroy(&(pool->queue_ready)); free (pool); /*銷毀后指針置空是個(gè)好習(xí)慣*/ pool=NULL; return 0; } void *thread_routine (void *arg) { printf ("starting thread 0x%x\n", pthread_self ()); while (1) { pthread_mutex_lock (&(pool->queue_lock)); /*如果等待隊(duì)列為0并且不銷毀線程池,則處于阻塞狀態(tài); 注意 pthread_cond_wait是一個(gè)原子操作,等待前會(huì)解鎖,喚醒后會(huì)加鎖*/ while (pool->cur_queue_size == 0 && !pool->shutdown) { printf ("thread 0x%x is waiting\n", pthread_self ()); pthread_cond_wait (&(pool->queue_ready), &(pool->queue_lock)); } /*線程池要銷毀了*/ if (pool->shutdown) { /*遇到break,continue,return等跳轉(zhuǎn)語(yǔ)句,千萬(wàn)不要忘記先解鎖*/ pthread_mutex_unlock (&(pool->queue_lock)); printf ("thread 0x%x will exit\n", pthread_self ()); pthread_exit (NULL); } printf ("thread 0x%x is starting to work\n", pthread_self ()); /*assert是調(diào)試的好幫手*/ assert (pool->cur_queue_size != 0); assert (pool->queue_head != NULL); /*等待隊(duì)列長(zhǎng)度減去1,并取出鏈表中的頭元素*/ pool->cur_queue_size--; CThread_worker *worker = pool->queue_head; pool->queue_head = worker->next; pthread_mutex_unlock (&(pool->queue_lock)); /*調(diào)用回調(diào)函數(shù),執(zhí)行任務(wù)*/ (*(worker->process)) (worker->arg); free (worker); worker = NULL; } /*這一句應(yīng)該是不可達(dá)的*/ pthread_exit (NULL); } // 下面是測(cè)試代碼 void *myprocess (void *arg) { printf ("threadid is 0x%x, working on task %d\n", pthread_self (),*(int *) arg); sleep (1);/*休息一秒,延長(zhǎng)任務(wù)的執(zhí)行時(shí)間*/ return NULL; } int main (int argc, char **argv) { pool_init (3);/*線程池中最多三個(gè)活動(dòng)線程*/ /*連續(xù)向池中投入10個(gè)任務(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; }結(jié)果如圖:
調(diào)研結(jié)果來源于:
http://blog.csdn.net/al_xin/article/details/39258067
http://blog.csdn.net/hubi0952/article/details/8045094
總結(jié)
以上是生活随笔為你收集整理的Linux进程池、线程池调研的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#语言实例源码系列-实现Word转换R
- 下一篇: 监听支付宝、微信 二合一 个人免签最新,