线程池的基本原理:
在传统服务器中,常有一个总监听进程监听有无新的用户连接服务器,每当有一个新的用户接入,服务器就开启一个新的线程用户处理这个用户的数据包,这个线程只服务于这个用户,当用户与服务器端关闭连接以后,服务器端销毁这个线程。然而,频繁的开启与关闭服务进程极大的占用系统资源,而且在大量用户情况下,系统为了开辟和销毁线程浪费大量时间和资源,线程池提供一个解决外部大量用户与服务器有限资源矛盾的方法,线程池和传统的一个用户对应一个线程的处理方法不同,它的基本思想就是在程序开始时在内存中开辟一些线程,线程的数目是固定的,它们独自形成一个类,屏蔽对外的操作,而服务器只需要将数据包交给线程池就可以了,当有新的客户请求到达时,不是新创建一个线程为其服务,而是从“池子”中挑选一个空闲的线程为新的客户请求服务,服务完毕后线程进入空闲池中,如果没有线程空闲的话,就将数据包暂时积累,等待线程池内有线程空闲在做处理,通过多个任务重用已经存在的线程对象,降低了对线程的创建和销毁的开销,当客户请求时线程对象已经存在,可以提高请求的响应时间。
如果一个应用需要频繁的创建和销毁线程,而任务执行的时间又非常短,这样线程销毁和创建的开销就不容小视,如果线程创建和销毁的时间比起任务执行时间忽略不计,则没必要使用线程池。
一个线程池主要包括以下几个部分:
1) 线程管理器用于创建并管理线程池。
2) 工作线程:线程池中实际执行任务的线程。在初始化过程中会预先创建好固定数目
的线程在池中,这些初始化线程一般处于空闲状态,一般不占用CPU,较小内存空间。
3)任务接口:每个任务必须实现的接口,当线程池的任务队列中有可执行任务时,被空置的工作线程调去执行(线程的闲与忙是通过互斥量实现的),把任务抽象出来形成接口,可以做到线程池与具体任务无关。
4)任务队列:用来存放没有处理的任务,提供一种缓冲机制,实现这种结构有好几种方法,常用的是队列,主要是先入先出,另外一种是链表数据结构,可以动态的为他分配内存空间。
下面是Linux系统下用C语言创建的线程池,线程池会维护一个任务链表(每个CThread_work结构就是一个任务),pool_init()预先创建好max_thread_num个线程,每个线程执行thread_routine()函数,该函数中:
while(pool->cur_queue_size == 0)
{
pthread_cond_wait(&(pool->queue_ready) , &(pool->queue_lock)) ;
}
表示如果任务链表中没有任务,则该线程处于阻塞等待状态,否则从任务中取得队列并执行。
pool_add_write向线程池的任务链表中加入任务,加入后调用pthread_cond_signal(&(pool->queue_ready))唤醒一个处于阻塞状态的线程。
pool_destroy()函数用于销毁线程池,线程池任务表中的任务不再被执行,但是正在运行的线程会一直把任务运行完后退出。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<pthread.h>
#include<assert.h>
/* 线程池里所有运行和等待的任务都是一个CThread_worker
所有任务在链表里所以是链表结构 */
struct CThread_worker {
//回调函数,任务运行时会调用此函数
void (*process) (void *arg) ;
void *arg ; //回调函数参数
struct CThread_worker *next ;
} ;
//线程池结构
struct CThread_pool {
pthread_mutex_t queue_lock ;
pthread_cond_t queue_ready ;
//链表任务,线程池中所有等待任务。
struct CThread_worker *queue_head ;
int shutdown ;//是否销毁线程池
pthread_t *threadid ;
//线程池中允许的活动线程数目
int thread_max_num ;
//当前等待队列任务数目
int cur_queue_size ;
} ;
int pool_add_worker (void *(*process)(void *arg) , void *arg) ;
void *thread_routine (void *arg) ;
//共享资源
static struct CThread_pool *pool = NULL ;
void pool_init (int thread_max_num)
{
pool = (struct CThread_pool *)malloc(sizeof(struct CThread_pool)) ;
pthread_mutex_init (&(pool->queue_lock) , NULL) ;
pthread_cond_init (&(pool->queue_ready) , NULL) ;
pool->queue_head = NULL ;
pool->thread_max_num = thread_max_num ;
pool->cur_queue_size = 0;
pool->shutdown = 0;
pool->threadid = (pthread_t *)malloc(thread_max_num * sizeof(pthread_t)) ;
int i = 0;
for(i = 0; i < thread_max_num ; i++)
{
pthread_create(&(pool->threadid[i]) , NULL, thread_routine , NULL) ;
}
}
//向线程池中加入任务
int pool_add_worker (void *(*process) (void *arg) , void *arg)
{
//构造一个新的任务
struct CThread_worker *newwork = (struct CThread_worker *)malloc(sizeof(struct CThread_worker));
newwork->process = process ;
newwork->arg = arg ;
newwork->next = NULL ; //别忘置空
pthread_mutex_lock (&(pool->queue_lock)) ;
//将任务加入到等待队列
struct CThread_worker *member = pool->queue_head ;
if(member != NULL) {
while(member->next != NULL)
{
member = member->next ;
}
member->next = newwork ;
} else {
pool->queue_head = newwork;
}
assert(pool->queue_head != NULL) ;
pool->cur_queue_size++ ;
pthread_mutex_unlock(&(pool->queue_lock)) ;
//等待队列中有任务唤醒一个等待线程, 如果所有线程都在忙碌,这句没有任何作用
pthread_cond_signal(&(pool->queue_ready)) ;
return 0;
}
/*销毁线程池,等待队列中的任务不再被执行,但正在运行的线程会一直把任务运行完后退出*/
int pool_destroy ()
{
if(pool->shutdown)
return -1 ; //防止销毁后调用
pool->shutdown = 1;
//唤醒所有的等待线程,线程池要摧毁了
pthread_cond_broadcast(&(pool->queue_ready)) ;
//阻塞等待线程退出,否则就成僵尸了
int i ;
for(i = 0; i < pool->thread_max_num ; i++)
{
pthread_join(pool->threadid[i], NULL) ;
}
free(pool->threadid) ;
//销毁等待队列
struct CThread_work *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) ;
//销毁后指针置空
pool = NULL ;
return 0;
}
void* thread_routine(void *arg)
{
printf("thread id is %u\n" , (unsigned int)pthread_self() );
while ( 1 )
{
pthread_mutex_lock (&(pool->queue_lock)) ;
/*如果等待队列为0并且不消毁线程池,则处于阻塞状态,注意pthread_cond_wait是一个原子操作*/
while(pool->cur_queue_size == 0 && !pool->shutdown)
{
printf("thread %u is waiting\n", (unsigned int)pthread_self()) ;
pthread_cond_wait(&(pool->queue_ready) , &(pool->queue_lock)) ;
}
//线程池要销毁了
if (pool -> shutdown)
{
//遇到跳转语句要先解锁
pthread_mutex_unlock (&(pool->queue_lock)) ;
printf("thread %u exit \n" , (unsigned int)pthread_self()) ;
pthread_exit (NULL ) ;
}
printf("thread %u is starting to work \n", (unsigned int)pthread_self()) ;
assert (pool->cur_queue_size != 0) ;
assert(pool->queue_head != NULL) ;
//等待队列长度减一,并取出链表头元素
pool->cur_queue_size -- ;
struct CThread_worker *worker = pool->queue_head ;
pool->queue_head = worker->next ;
pthread_mutex_unlock(&(pool->queue_lock)) ;
//执行任务
(*(worker->process)) (worker->arg ) ;
free(worker) ;
worker = NULL;
}
}
void * myprocess (void *arg)
{
printf("thread %u , working on task %d\n" , (unsigned int)pthread_self() , *(int *) arg) ;
sleep(1) ;
return NULL ;
}
int main (int argc, char **argv)
{
pool_init (3) ; //线程池中有三个线程
//连续向线程池中投入10个任务
int *worknum = (int *)malloc(10 * sizeof(int)) ;
int i ;
printf ("do 10 task to the thread pool \n") ;
for( i = 0; i < 9; i++)
{
worknum[i] = i ;
pool_add_worker (myprocess , &worknum[i]) ;
}
//等待所有任务完成
sleep (10) ;
pool_destroy( );
free (worknum) ;
return 0 ;
}