线程池学习

线程池的基本原理:

在传统服务器中,常有一个总监听进程监听有无新的用户连接服务器,每当有一个新的用户接入,服务器就开启一个新的线程用户处理这个用户的数据包,这个线程只服务于这个用户,当用户与服务器端关闭连接以后,服务器端销毁这个线程。然而,频繁的开启与关闭服务进程极大的占用系统资源,而且在大量用户情况下,系统为了开辟和销毁线程浪费大量时间和资源,线程池提供一个解决外部大量用户与服务器有限资源矛盾的方法,线程池和传统的一个用户对应一个线程的处理方法不同,它的基本思想就是在程序开始时在内存中开辟一些线程,线程的数目是固定的,它们独自形成一个类,屏蔽对外的操作,而服务器只需要将数据包交给线程池就可以了,当有新的客户请求到达时,不是新创建一个线程为其服务,而是从“池子”中挑选一个空闲的线程为新的客户请求服务,服务完毕后线程进入空闲池中,如果没有线程空闲的话,就将数据包暂时积累,等待线程池内有线程空闲在做处理,通过多个任务重用已经存在的线程对象,降低了对线程的创建和销毁的开销,当客户请求时线程对象已经存在,可以提高请求的响应时间。

如果一个应用需要频繁的创建和销毁线程,而任务执行的时间又非常短,这样线程销毁和创建的开销就不容小视,如果线程创建和销毁的时间比起任务执行时间忽略不计,则没必要使用线程池。

一个线程池主要包括以下几个部分:

1) 线程管理器用于创建并管理线程池。

2) 工作线程:线程池中实际执行任务的线程。在初始化过程中会预先创建好固定数目

的线程在池中,这些初始化线程一般处于空闲状态,一般不占用CPU,较小内存空间。

3)任务接口:每个任务必须实现的接口,当线程池的任务队列中有可执行任务时,被空置的工作线程调去执行(线程的闲与忙是通过互斥量实现的),把任务抽象出来形成接口,可以做到线程池与具体任务无关。

4)任务队列:用来存放没有处理的任务,提供一种缓冲机制,实现这种结构有好几种方法,常用的是队列,主要是先入先出,另外一种是链表数据结构,可以动态的为他分配内存空间。

下面是Linux系统下用C语言创建的线程池,线程池会维护一个任务链表(每个CThread_work结构就是一个任务),pool_init()预先创建好max_thread_num个线程,每个线程执行thread_routine()函数,该函数中:

whilepool->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 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值