实验目的
- 了解线程池的原理
- 了解线程池解决什么问题
- 掌握线程池的实现
实验内容
- 初始化线程池,在其中创建线程
- 执行完所有线程任务后,销毁线程池
实验原理
- 在传统服务器结构中,常是有一个总的监听线程监听有没有新的用户连接服务器,每当有一个新的用户进入,服务器就开启一个新的线程用户处理这 个用户的数据包。这个线程只服务于这个用户,当用户与服务器端关闭连接以后,服务器端销毁这个线程。
- 然而频繁地开辟与销毁线程极大地占用了系统的资源,而且在大量用户的情况下,系统为了开辟和销毁线程将浪费大量的时间和资源。线程池提供了一个解决外部大量用户与服务器有限资源的矛盾。
- 线程池和传统的一个用户对应一个线程的处理方法不同,它的基本思想就是在程序开始时就在内存中开辟一些线程,线程的数目是固定的,他们独自形成一个类,屏蔽了对外的操作,而服务器只需要将数据包交给线程池就可以了。当有新的客户请求到达时,不是新创建一个线程为其服务,而是从“池子”中选择一个空闲的线程为新的客户请求服务,服务完毕后,线程进入空闲线程池中。如果没有线程空闲的话,就将数据包暂时积累, 等待线程池内有线程空闲以后再进行处理。通过对多个任务重用已经存在的线程对象,降低了对线程对象创建和销毁的开销。当客户请求 时,线程对象已经存在,可以提高请求的响应时间,从而整体地提高了系统服务的表现。
程序设计
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
struct job
{
void *(*func)(void *arg);
void *arg;
struct job *next;
};
struct threadpool
{
int thread_num;//已开启的线程数量
pthread_t *pthread_ids;//保存线程池中线程id
struct job *head;//任务队列的头指针
struct job *tail;//任务队列的尾指针
int queue_max_num;//任务队列的最大数
int queue_cur_num;//任务队列已有多少任务
pthread_mutex_t mutex;
pthread_cond_t queue_empty;//任务队列为空的条件
pthread_cond_t queue_not_empty;//任务队列不为空的条件
pthread_cond_t queue_not_full;//任务队列不为满的条件
};
//线程函数
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) //任务队列是否为空
{
pthread_cond_wait( &(pool->queue_not_empty), &(pool->mutex) );
}
pjob = pool->head;
pool->queue_cur_num --;
if(pool->queue_cur_num != pool->queue_max_num)
{
pthread_cond_broadcast( &(pool->queue_not_empty) );
}
if ( pool->queue_cur_num==0 )
{
pool->head = pool->tail = NULL ;
pthread_cond_broadcast( &(pool->queue_empty) );
}
else
{
pool->head = pool->head->next ;
}
pthread_mutex_unlock( &(pool->mutex) );
(*(pjob->func))(pjob->arg);
free(pjob);
pjob = NULL ;
}
}
//线程池初始化
struct threadpool *threadpool_init( int thread_num,int queue_max_num)
{
struct threadpool *pool=( struct threadpool * )malloc( sizeof(struct threadpool) );
//malloc
//int thread_num = 20;
//int queue_max_num = 100;
pool->thread_num = thread_num ;
pool->queue_max_num = queue_max_num;
pool->queue_cur_num = 0;
pool ->head = NULL;
pool->tail = NULL;
pthread_mutex_init( &(pool->mutex) ,NULL );
pthread_cond_init( &(pool->queue_empty), NULL );
pthread_cond_init( &(pool->queue_not_empty) , NULL );
pthread_cond_init( &(pool->queue_not_full) , NULL );
pool->pthread_ids = (pthread_t *) malloc( sizeof(pthread_t) * thread_num );
//malloc
for ( int i = 0; i < pool->thread_num ; i++)
{
pthread_create( &(pool->pthread_ids[i ]) , NULL , threadpool_function , (void *)pool );
}
return pool;
}
//添加任务函数
void threadpool_add_job(struct threadpool *pool,void *func(void *) , void * arg )
{
pthread_mutex_lock(&(pool->mutex));
while(pool->queue_cur_num == pool->queue_max_num)
{
pthread_cond_wait( &(pool->queue_not_full), &(pool->mutex) );
}
struct job *pjob = (struct job *)malloc ( sizeof(struct job));
//malloc
pjob->func = func ;
pjob->arg = arg;
if(NULL == pool->head)
{
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) );
}
void * work ( void * arg )
{
char * p =(char *)arg;
printf("hello world %s\n",p);
printf("welcome to china %s\n",p);
sleep(1);
}
//资源回收
void threadpool_destroy (struct threadpool *pool )
{
pthread_mutex_lock(&(pool->mutex) );
while( pool->queue_cur_num != 0 )
{
pthread_cond_wait( &(pool->queue_empty) , &(pool->mutex) );
}
pthread_mutex_unlock( &(pool->mutex) );
pthread_cond_broadcast(&(pool->queue_empty) );
pthread_cond_broadcast(&(pool->queue_not_empty) );
pthread_cond_broadcast(&(pool->queue_not_full) );
free(pool->pthread_ids);
for( int i = 0 ; i< pool->thread_num ; i++)
{
pthread_cancel(pool->pthread_ids[i] );
pthread_join(pool->pthread_ids[i] ,NULL );
}
struct job *temp;
while(pool->head !=NULL )
{
temp = pool->head;
pool->head = temp->next;
free(temp);
}
free(pool);
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));
}
int main()
{
struct threadpool * pool = threadpool_init( 10,100 );
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 , "22" );
threadpool_add_job( pool , work , "33" );
threadpool_add_job( pool , work , "44" );
threadpool_add_job( pool , work , "55" );
threadpool_add_job( pool , work , "66" );
threadpool_add_job( pool , work , "77" );
threadpool_add_job( pool , work , "88" );
threadpool_add_job( pool , work , "99" );
threadpool_add_job( pool , work , "100" );
sleep(50);
threadpool_destroy(pool );
return 0;
}
运行结果
root@jsetc-virtual-machine:/0504# ./xcc
hello world 2
welcome to china 2
hello world 10
welcome to china 10
hello world 4
welcome to china 4
hello world 3
welcome to china 3
hello world 5
welcome to china 5
hello world 1
welcome to china 1
hello world 6
welcome to china 6
hello world 7
welcome to china 7
hello world 9
welcome to china 9
hello world 8
welcome to china 8
hello world 11
welcome to china 11
hello world 22
welcome to china 22
hello world 44
welcome to china 44
hello world 55
hello world 66
welcome to china 66
welcome to china 55
hello world 33
welcome to china 33
hello world 77
welcome to china 77
hello world 88
welcome to china 88
hello world 99
welcome to china 99
hello world 100
welcome to china 100
^C
实验总结
要是一个应用需要频繁的创建和销毁线程,任务执行的时间又比较短的话,就轮到线程池出场,但是线程创建和销毁的时间相比任务执行时间少很多的话,就没有必要用线程池了。