线程池

线程池:
线程池的应用范围:
线程池的应用范围:
需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
对性能要求苛刻的应用,比如要求服务器迅速相应客户请求。
接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,并出现"OutOfMemory"的错误
线程池实现原理:
利用了条件变量和互斥锁共同来完成,如果仅有锁 pthread_lock(); 那么第二次试图去上锁的话,程序会阻塞到pthread_lock();的位置
而如果利用了条件变量pthread_cont_wait();在pthread_cont_wait内部会有一个unlock,直到其他任务发出了pthread_cont_singal()的时候会唤醒当前处于等待的一个线程,在pthread_cont_wait离开之后,又会加锁;而在某个任务发出pthread_cont_signal的时候,处于pthread_cont_wait()的线程不会立即去响应,要等待处于pthread_cont_wait的线程处于执行态的时候才回去响应pthread_cont_signal发出来的信号
线程池的linux c 实现:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include <assert.h>

/*
*线程池里所有运行和等待的任务都是一个CThread_worker
*由于所有任务都在链表里,所以是一个链表结构
*/
typedef struct worker
{
/*回调函数,任务运行时会调用此函数,注意也可声明成其它形式*/
void *(*process)(void *arg);
void *arg; /*回调函数的参数*/
struct worker *next;
}CThread_worker;


/*线程池结构*/
typedef struct
{
pthread_mutex_t queue_lock;
pthread_cond_t queue_ready;

/*链表结构,线程池中所有等待任务*/
CThread_worker *queue_head;

/*是否销毁线程池*/
int shutdown;
pthread_t *threadid;
/*线程池中允许的活动线程数目*/
int max_thread_num;
/*当前等待队列的任务数目*/
int cur_queue_size;
}CThread_pool;


int pool_add_worker(void *(*process) (void *arg), void *arg);
void *thread_routine(void *arg);

static CThread_pool *pool = NULL;

/*初始化线程池结构体并创建线程*/
void pool_init(int max_thread_num)
{
printf("into the pool_init\n");
/*创建线程池结构体*/
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;

/*创建存放线程ID的空间*/
pool->threadid = (pthread_t *)malloc(max_thread_num * sizeof (pthread_t));
int i = 0;

/*循环创建MAX_THREAD_NUM个线程*/
for (i = 0; i < max_thread_num; i++)
{
printf("create the create\n");
pthread_create(&(pool->threadid[i]), NULL, thread_routine,NULL);
}
sleep(1);
printf("pool_init is end\n");
return;
}


/*向线程池中加入任务*/
int pool_add_worker(void *(*process) (void *arg), void *arg)
{
printf("into the pool_add_worker\n");
/*构造一个新任务*/
CThread_worker *newworker = (CThread_worker *)malloc(sizeof (CThread_worker));
newworker->process = process;//将回调函数赋值给新任务
newworker->arg = arg;
newworker->next = NULL;/*别忘置空*/

pthread_mutex_lock(&(pool->queue_lock));
/*将任务加入到等待队列中*/
CThread_worker *member = pool->queue_head;
if(member != NULL)
{
//将任务加入到任务链连的最后位置.
while(member->next != NULL)
member = member->next;
member->next = newworker;
}
else
{
//如果是第一个任务的话,就指向头
pool->queue_head = newworker;
}
// pool->queue_head = NULL;
assert(pool->queue_head != NULL);

pool->cur_queue_size++;
pthread_mutex_unlock (&(pool->queue_lock));
/*好了,等待队列中有任务了,唤醒一个等待线程;
注意如果所有线程都在忙碌,这句没有任何作用*/
if (!pthread_cond_signal (&(pool->queue_ready))) //发出信号不一定立即去响应,要等到其他线程占有CPU时间片才行
printf("succeed\n");
printf("after signal...\n");
printf("pool->cur_queue_size=%d\n", pool->cur_queue_size);
return 0;
}


/*销毁线程池,等待队列中的任务不会再被执行,但是正在运行的线程会一直
把任务运行完后再退出*/
int pool_destroy(void)
{
if (pool->shutdown)
return -1;/*防止两次调用*/
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);

/*销毁等待队列*/
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);
/*销毁后指针置空是个好习惯*/
pool = NULL;
return 0;
}

//功能:维护线程池
void *thread_routine(void *arg)
{
printf("into the thread_routine\n");
//开始线程,通过pthread_self()来获取线程ID.
printf ("starting thread 0x%lx,%d,%d\n", pthread_self(),pool->cur_queue_size,pool->shutdown);
while(1)
{
pthread_mutex_lock(&(pool->queue_lock));
/*如果等待队列为0并且不销毁线程池,则处于阻塞状态;
注意:pthread_cond_wait是一个原子操作,等待前会解锁,唤醒后会加锁*/
while((pool->cur_queue_size == 0) && (!pool->shutdown))
{
printf ("thread 0x%lx is waiting\n", pthread_self ());
//条件变量
pthread_cond_wait(&(pool->queue_ready), &(pool->queue_lock));
printf("later\n");
}

/*线程池要销毁了*/
if(pool->shutdown)
{
/*遇到break,continue,return等跳转语句,千万不要忘记先解锁*/
pthread_mutex_unlock (&(pool->queue_lock));
printf ("thread 0x%lx will exit\n", pthread_self());
pthread_exit(NULL);
}

printf ("thread 0x%lx is starting to work\n", pthread_self ());

/*assert是调试的好帮手*/
assert (pool->cur_queue_size != 0);
assert (pool->queue_head != NULL);

/*等待队列长度减去1,并取出链表中的头元素*/
pool->cur_queue_size--;
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; //指针free之后,一定要置空NULL
}
/*这一句应该是不可达的*/
pthread_exit(NULL);
return NULL;
}

void *myprocess (void *arg)
{
printf("threadid is 0x%lx, working on task %d\n", pthread_self (),*(int *) arg);
sleep(1);/*休息一秒,延长任务的执行时间*/
return NULL;
}

int main (int argc, char *argv[])
{
pool_init(3); /*线程池中最多三个活动线程*/

/*连续向池中投入10个任务*/
int *workingnum = (int *)malloc(sizeof(int) * 10);
int i = 0;
for(i = 0; i < 10; i++)
{
workingnum[i] = i;
pool_add_worker(myprocess, &workingnum[i]);
}
/*等待所有任务完成*/
sleep(5); //主线程处于就绪态,让线程池里面的三个线程有机会处于CPU的执行态
/*销毁线程池*/
pool_destroy();
free (workingnum);
return 0;
}
代码收获:
本程序 在出错检测和资源释放的处理方面做的非常好!
1 assert(ptr != NULL) 让断言成为你的朋友
2 用过的资源一定记着回收
参考地址:
http://www.ibm.com/developerworks/cn/java/l-threadPool/
http://hi.baidu.com/bailaoheidu/blog/item/029c39778502e610b051b94a.html

posted on 2012-01-05 16:12  lightsalt2011  阅读(652)  评论(0编辑  收藏  举报

导航