1、什么是线程池?
线程池是一种使用多线程与线程同步互斥机制的一种组织结构,这种结构在处理大批量同类型任务时是一种比较高效的处理方式。
线程池本质时一个结构体,管理一批线程和线程需要执行的任务。
面向对象编程中,对象创建和销毁是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是对一些很耗资源的对象创建和销毁。如何利用已有对象来服务就是一个需要解决的关键问题,其实这就是一些”池化资源”技术产生的原因。比如大家所熟悉的数据库连接池就是遵循这一思想而产生的,下面将介绍的线程池技术同样符合这一思想。
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。但如果对多线程应用不当,会增加对单个任务的处理时间。可以举一个简单的例子:
假设一台服务器完成一项任务的时间为T
T1 创建线程的时间
T2 在线程中执行任务的时间,包括线程间同步所需时间
T3 线程销毁的时间
显然T = T1+T2+T3。注意这是一个极度简化的假设。
可以看出T1,T3是多线程本身附加的开销,用户希望减少T1,T3所用的时间,从而减少T的时间。但一些线程的使用者并没有注意到这一点,所以在应用程序中频繁的创建或销毁线程,这导致T1和T3在T中占有非常大的比例。
线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1、T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1、T3的开销了,线程池不仅调整T1、T3产生的时间,而且它还显著减少了创建线程的数目。在看一个例子:
假设一台服务器每天大约要处理100000个请求,并且每个请求需要一个单独的线程完成,这是一个很常用的场景。在线程池中,线程数量一般是固定的,所以产生线程总数不会超过线程池中线程的数目或者上限,而如果服务器不利用线程池来处理这些请求则线程总数为100000。一般线程池尺寸是远小于100000。所以利用线程池的服务器程序不会为了创建100000而在处理请求时浪费时间,从而提高效率。
线程池是一种多线程处理方法,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程,每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程处于空闲状态,则线程池将会调度一个任务给它,如果所有线程都始终保持繁忙,但将任务放入到一个队列中,则线程池将在一段时间后创建另一个辅助线程,但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。
线程池主要有如下几个应用范围:
1、需要大量的线程来完成任务,且完成任务的时间比较短,如WEB服务器完成网页请求这样的任务。因为单个任务小,而任务数量巨大,比如一个热门网站的点击次数。 但对于长时间的任务,比如一个ftp连接请求,线程池的优点就不明显了。因为ftp会话时间相对于线程的创建时间长多了。
2、对性能要求苛刻的应用,比如要求服务器迅速相应客户请求。
3、接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限。
代码例子:
thread_pool.c
#include "thread_pool.h"
void handler(void *arg) //线程退出处理函数,相当于 进程的atexit
{
pthread_mutex_unlock((pthread_mutex_t *)arg); //解锁互斥锁
}
void *routinue(void *arg) //每一条线程执行函数
{
thread_pool *pool = (thread_pool *)arg;
struct task *p;
while(1)
{
pthread_cleanup_push(handler, (void *)&pool->lock); //注册线程退出函数
pthread_mutex_lock(&pool->lock); //上锁
while(pool->waiting_tasks == 0 && !pool->shutdown)
{
pthread_cond_wait(&pool->cond, &pool->lock); //没有任务,销毁标志为假,进入睡眠
}
if(pool->waiting_tasks == 0 && pool->shutdown) //任务数量为0,销毁标志为真,退出线程。
{
pthread_mutex_unlock(&pool->lock); //解锁
pthread_exit(NULL); //退出线程
}
p = pool->task_list->next;
pool->task_list->next = p->next; //取出任务
pool->waiting_tasks--; //任务数-1
pthread_mutex_unlock(&pool->lock); //解锁
pthread_cleanup_pop(0); //取消线程退出函数
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); //退出请求屏蔽
(p->task)(p->arg); //执行任务函数
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); //恢复退出请求
free(p); //释放p空间
}
pthread_exit(NULL);
}
/*线程池初始化:pool:线程池结构体指针,thread_number:初始化的线程数量*/
bool init_pool(thread_pool *pool, unsigned int thread_number)
{
pthread_mutex_init(&pool->lock, NULL);
pthread_cond_init(&pool->cond, NULL); //初始化互斥锁和条件变量
pool->shutdown = false; //销毁标志为假
pool->task_list = malloc(sizeof(struct task)); //任务队列头结点
pool->tids = malloc(sizeof(pthread_t) * MAX_ACTIVE_THREADS); //申请存放线程ID的堆空间
if(pool->task_list == NULL || pool->tids == NULL) //出错处理
{
perror("malloc failed");
return false;
}
pool->task_list->next = NULL;
pool->waiting_tasks = 0; //任务数
pool->active_threads = thread_number; //线程数
for(int i = 0; i<thread_number; i++) //循环创建线程
{
if(pthread_create(&pool->tids[i], NULL, routinue, (void*)pool)!=0) //每个线程都执行一样的函数
{
perror("pthread_create failed");
return false;
}
}
return true;
}
//投放任务 ==> 尾插法添加任务到链表(任务队列)
bool add_task(thread_pool *pool, void*(*task)(void *arg), void *arg)
{
struct task *new_task = malloc(sizeof(struct task));
if(new_task == NULL)
{
perror("malloc faoled");
return false;
}
new_task->task = task;
new_task->arg = arg;
new_task->next = NULL;
/*访问任务队列,先上锁*/
pthread_mutex_lock(&pool->lock);
if(pool->waiting_tasks >= MAX_WAITING_TASKS) //判断当前任务队列中的任务数是否到达最大值
{
pthread_mutex_unlock(&pool->lock);
fprintf(stderr, "too many task...");
return false;
}
struct task *tmp = pool->task_list; //添加新的任务节点
while(tmp->next != NULL)
tmp = tmp->next; //tmp指向任务队列的尾部节点
tmp->next = new_task; //把新节点插入到尾节点下一个
pool->waiting_tasks++;
pthread_mutex_unlock(&pool->lock); //解锁
pthread_cond_signal(&pool->cond); //唤醒任意一个睡眠的线程
return true;
}
/*添加线程 pool : 线程池变量地址; additions_threads_number: 需要添加的线程数量*/
int add_thread(thread_pool *pool, unsigned int additions_threads_number)
{
if(additions_threads_number == 0) //添加线程数为 0
{
return 0;
}
unsigned int total_thread = pool->active_threads + additions_threads_number;
int i, actual_incrment = 0; //成功添加的线程数量
for(i = pool->active_threads; i<total_thread && i<MAX_ACTIVE_THREADS; i++)
{
if(pthread_create(&pool->tids[i], NULL, routinue, (void *)pool)!=0) //每个线程都执行一样的函数
{
perror("add pthread failed");
if(actual_incrment == 0)
return -1;
break;
}
actual_incrment++; //成功创建的线程数
}
pool->active_threads += actual_incrment;
return actual_incrment;//成功添加的线程数量
}
int remove_thread(thread_pool *pool, unsigned int removing_threads_number)
{
if(removing_threads_number == 0)
return pool->active_threads;
int remain_threads = pool->active_threads - removing_threads_number;
remain_threads = remain_threads > 0 ? remain_threads : 1 ;
int i;
for(i= pool->active_threads -1; i >remain_threads-1; i--)
{
errno = pthread_cancel(pool->tids[i]); //从后往前取消线程
if(errno != 0)
break;
}
if(i == pool->active_threads -1)
{
return -1;
}
else
{
pool->active_threads = i+1;
return i+1; //剩下的线程数
}
}
/*线程池销毁*/
bool destroy_pool(thread_pool *pool)
{
pool->shutdown = true;
pthread_cond_broadcast(&pool->cond); //唤醒所有线程
int i;
for(i=0; i<pool->active_threads; i++) //回收所有线程资源
{
errno = pthread_join(pool->tids[i], NULL);
if(errno != 0)
{
printf("join tids[%d] error:%s\n", i, strerror(errno));
}
else
printf("[%d] is joined \n", (unsigned)pool->tids[i]);
}
free(pool->task_list);
free(pool->tids);
free(pool);
return true;
}
void *count_time(void *arg) //计时
{
int i = 0;
while(1)
{
printf("sec:%d\n", ++i);
sleep(1);
}
}
void *mytask(void *arg) //测试功能函数:传递一个整型参数n,n秒之后结束
{
int n = *(int *)arg;
printf("task[%ld] will join by %d s\n", pthread_self(), n);
while(n--)
{
sleep(1);
}
printf("task[%ld] is finish!\n", pthread_self());
}
thread_pool.h
#ifndef _THREAD_POOL_H_
#define _THREAD_POOL_H_
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <pthread.h>
#define MAX_WAITING_TASKS 1000 //任务最大数量 1000
#define MAX_ACTIVE_THREADS 20 //最大活跃线程数 20
struct task{
void *(*task)(void *arg); //任务函数指针
void *arg; //任务函数参数
struct task *next;
};
typedef struct thread_pool
{
pthread_mutex_t lock; //互斥锁
pthread_cond_t cond; //条件变量
struct task *task_list; //任务队列头结点
pthread_t *tids; //线程ID首地址
unsigned int waiting_tasks; //待处理任务数
unsigned int active_threads; //活跃线程数
bool shutdown; //开关标志
}thread_pool;
/*初始化线程池 pool:线程池指针, thread_number:创建的线程数 */
bool init_pool(thread_pool *pool, unsigned int thread_number);
bool add_task(thread_pool *pool, void*(*task)(void *arg), void *arg);
int add_thread(thread_pool *pool, unsigned int additions_threads_number);
int remove_thread(thread_pool *pool, unsigned int removing_threads_number);
bool destroy_pool(thread_pool *pool);
void *mytask(void *arg); //任务函数 ==> 用户定义的函数
void *count_time(void *arg);
#endif
main.c
#include "thread_pool.h"
int main()
{
int num;
pthread_t t1;
pthread_create(&t1, NULL, count_time, NULL); //创建一条线程去计时
thread_pool *pool = malloc(sizeof(thread_pool)); //给线程池指针申请空间
init_pool(pool, 3); //初始化线程池,创建3条线程
//投放任务
printf("throwing 3 tasks...\n");
num = (rand()%10); //获取一个10以内的随机数
add_task(pool, mytask, (void *)&num);
num = (rand()%10);
add_task(pool, mytask, (void *)&num);
num = (rand()%10);
add_task(pool, mytask, (void *)&num);
printf("current thread number:%d\n", pool->active_threads);
sleep(9); //延时9s所有线程处于睡眠状态
printf("throwing 2 tasks...\n"); //再投入2个任务
num = (rand()%10);
add_task(pool, mytask, (void *)&num);
num = (rand()%10);
add_task(pool, mytask, (void *)&num);
add_thread(pool, 5);
sleep(5);
printf("current thread number:%d\n", pool->active_threads);
//删除线程池
destroy_pool(pool);
return 0;
}