参考:https://blog.youkuaiyun.com/lmh12506/article/details/7753952
/*
什么时候需要创建线程池呢?简单的说,如果一个应用需要频繁的创建和销毁线程,而任务执行的时间又非常短,这样线程创建和销毁的带来的开销就不容忽视,这时也是线程池该出场的机会了。如果线程创建和销毁时间相比任务执行时间可以忽略不计,则没有必要使用线程池了。
下面是Linux系统下用C语言创建的一个线程池。线程池会维护一个任务链表(每个PthreadWorker结构就是一个任务)。
pool_init()函数预先创建好max_thread_num个线程,每个线程执thread_routine ()函数。该函数中
while (pool->cur_queue_size == 0)
{
pthread_cond_wait (&(pool->queue_ready),&(pool->queue_lock));
}
表示如果任务链表中没有任务,则该线程出于阻塞等待状态。否则从队列中取出任务并执行。
pool_add_worker()函数向线程池的任务链表中加入一个任务,加入后通过调用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>
#define PTHREAD_TASK_MAX_NUM 15
/*
*线程池里所有运行和等待的任务都是一个 PthreadWorker
*由于所有任务都在链表里,所以是一个链表结构
*/
typedef struct worker
{
int type;
void *(*process)(void *arg); /*回调函数,任务运行时会调用此函数,注意也可声明成其它形式*/
void *arg; /*回调函数的参数*/
struct worker *next; /* 线程池任务链表 */
} PthreadWorker;
/*线程池结构*/
typedef struct
{
pthread_mutex_t queue_lock;
pthread_cond_t queue_ready;
PthreadWorker *queue_head; /*链表结构,线程池中所有等待任务,队列头*/
int shutdown; /*是否销毁线程池*/
pthread_t *threadid;
int max_thread_num; /*线程池中允许的活动线程数目*/
int cur_queue_size; /*当前等待队列的任务数目*/
} PthreadPool;
int pool_add_worker(void *(*process)(void *arg), void *arg);
void *thread_routine(void *arg);
static PthreadPool *g_pool = NULL;
// 创建线程池,并初始化
void pool_init(int max_thread_num)
{
g_pool =(PthreadPool *) malloc(sizeof(PthreadPool));
if(g_pool == NULL) {
printf("malloc thread g_pool failed");
return;
}
pthread_mutex_init(&(g_pool->queue_lock), NULL);
pthread_cond_init(&(g_pool->queue_ready), NULL);
g_pool->queue_head = NULL;
g_pool->max_thread_num = max_thread_num;
g_pool->cur_queue_size = 0;
g_pool->shutdown = 0;
g_pool->threadid =(pthread_t *) malloc(max_thread_num * sizeof(pthread_t));
int i = 0;
for (i = 0; i < max_thread_num; i++) {
printf("create pthread %d\n", i);
pthread_create(&(g_pool->threadid[i]), NULL, thread_routine, NULL);
}
}
/*向线程池中加入任务*/
// 即:将要处理的任务(函数、参数)插入到线程池的任务队列中:PthreadWorker *queue_head;
// 该函数需作为公共模块,供其他功能模块函数调用
int pool_add_worker(void *(*process)(void *arg), void *arg)
{
/* 1. 构造一个新任务*/
PthreadWorker *newWorker =(PthreadWorker *) malloc(sizeof(PthreadWorker));
// 构造任务头
newWorker->type = (*(int *)arg) % 3;
newWorker->process = process;
newWorker->arg = arg;
newWorker->next = NULL;/*别忘置空*/
pthread_mutex_lock(&(g_pool->queue_lock));
/* 2. 将任务加入到等待队列中*/
PthreadWorker *member = g_pool->queue_head;
if(member != NULL) {
while(member->next != NULL) {
member = member->next;
}
member->next = newWorker;
} else {
g_pool->queue_head = newWorker;
}
assert(g_pool->queue_head != NULL);
g_pool->cur_queue_size++;
printf("[%s], add task count: g_pool->cur_queue_size: %d\n", __FUNCTION__, g_pool->cur_queue_size);
pthread_mutex_unlock(&(g_pool->queue_lock));
/* 3. 等待队列中有任务了,唤醒一个等待线程;
注意:如果所有线程都在忙碌,这句没有任何作用 ????? */
pthread_cond_signal(&(g_pool->queue_ready));
return 0;
}
/*销毁线程池,等待队列中的任务不会再被执行,但是正在运行的线程会一直
把任务运行完后再退出*/
int pool_destroy()
{
if(g_pool->shutdown) {
return -1;/*防止两次调用*/
}
g_pool->shutdown = 1;
/*唤醒所有等待线程,线程池要销毁了*/
pthread_cond_broadcast(&(g_pool->queue_ready));
/*阻塞等待线程退出,否则就成僵尸了*/
int i;
for(i = 0; i < g_pool->max_thread_num; i++) {
pthread_join(g_pool->threadid[i], NULL);
}
free(g_pool->threadid);
/*销毁等待队列*/
PthreadWorker *head = NULL;
while(g_pool->queue_head != NULL) {
head = g_pool->queue_head;
g_pool->queue_head = g_pool->queue_head->next;
free(head);
}
/*条件变量和互斥量也别忘了销毁*/
pthread_mutex_destroy(&(g_pool->queue_lock));
pthread_cond_destroy(&(g_pool->queue_ready));
free(g_pool);
/*销毁后指针置空是个好习惯*/
g_pool=NULL;
return 0;
}
void *thread_routine(void *arg)
{
printf("starting thread 0x%x\n",(unsigned int)pthread_self());
while (1)
{
pthread_mutex_lock(&(g_pool->queue_lock));
/*如果等待队列为0并且不销毁线程池,则处于阻塞状态;
注意:pthread_cond_wait是一个原子操作,等待前会解锁,唤醒后会加锁*/
while (g_pool->cur_queue_size == 0 && !g_pool->shutdown) {
printf("thread 0x%x is waiting\n\n",(unsigned int)pthread_self());
pthread_cond_wait(&(g_pool->queue_ready), &(g_pool->queue_lock));
}
/*线程池要销毁了*/
if(g_pool->shutdown) {
/*遇到break,continue,return等跳转语句,千万不要忘记先解锁*/
pthread_mutex_unlock(&(g_pool->queue_lock));
printf("thread 0x%x will exit\n",(unsigned int)pthread_self());
pthread_exit(NULL);
}
printf("\nthread 0x%x is starting to work\n",(unsigned int)pthread_self());
assert(g_pool->cur_queue_size != 0);
assert(g_pool->queue_head != NULL);
/*等待队列长度减去1,并取出链表中的头元素*/
g_pool->cur_queue_size--;
printf("[%s], minus task count: g_pool->cur_queue_size: %d\n", __FUNCTION__, g_pool->cur_queue_size);
// 如下两句为取出队列中的任务
PthreadWorker *task = g_pool->queue_head;
g_pool->queue_head = task->next;
pthread_mutex_unlock(&(g_pool->queue_lock));
printf("******task type: %d is handle\n", task->type);
/*调用回调函数,执行任务*/
(*(task->process))(task->arg);
// 因任务都是通过 malloc(sizeof(PthreadWorker)) 申请内存插入任务队列的,故处理完一个任务需free()内存;
free(task);
task = NULL;
}
/*这一句应该是不可达的*/
pthread_exit(NULL);
}
【测试程序】
void *myprocess(void *arg)
{
printf("threadid is 0x%x, working on task: %d\n",(unsigned int)pthread_self(), *(int *) arg);
usleep(300000);/*休息一秒,延长任务的执行时间*/
printf("threadid is 0x%x, working on task: %d, end...\n",(unsigned int)pthread_self(), *(int *) arg);
return NULL;
}
int main(int argc, char **argv)
{
pool_init(3);/*线程池中最多三个活动线程*/
/*连续向池中投入 PTHREAD_TASK_MAX_NUM 个任务*/
int *workingnum =(int *) malloc(sizeof(int) * PTHREAD_TASK_MAX_NUM);
int i;
for (i = 0; i < PTHREAD_TASK_MAX_NUM; i++) {
workingnum[i] = i;
pool_add_worker(myprocess, &workingnum[i]);
}
/*等待所有任务完成*/
usleep(3000000);
printf("[%s], g_pool->cur_queue_size = %d\n", __FUNCTION__, g_pool->cur_queue_size);
/*销毁线程池*/
pool_destroy();
free(workingnum);
return 0;
}
【编译】
zll@zll-Lenovo:~/Pro_Soft$
zll@zll-Lenovo:~/Pro_Soft$ gcc 05pthread_pool.c -o 05pthread_pool -lpthread
zll@zll-Lenovo:~/Pro_Soft$
【运行结果】
zll@zll-Lenovo:~/Pro_Soft$ ./05pthread_pool
create pthread 0
create pthread 1
create pthread 2
starting thread 0x9be02700
thread 0x9be02700 is waiting
[pool_add_worker], add task count: g_pool->cur_queue_size: 1
[pool_add_worker], add task count: g_pool->cur_queue_size: 2
starting thread 0x9b601700
[pool_add_worker], add task count: g_pool->cur_queue_size: 3
[pool_add_worker], add task count: g_pool->cur_queue_size: 4
[pool_add_worker], add task count: g_pool->cur_queue_size: 5
[pool_add_worker], add task count: g_pool->cur_queue_size: 6
[pool_add_worker], add task count: g_pool->cur_queue_size: 7
[pool_add_worker], add task count: g_pool->cur_queue_size: 8
[pool_add_worker], add task count: g_pool->cur_queue_size: 9
[pool_add_worker], add task count: g_pool->cur_queue_size: 10
[pool_add_worker], add task count: g_pool->cur_queue_size: 11
[pool_add_worker], add task count: g_pool->cur_queue_size: 12
[pool_add_worker], add task count: g_pool->cur_queue_size: 13
thread 0x9b601700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 12
******task type: 0 is handle
threadid is 0x9b601700, working on task: 0
starting thread 0x9c603700
thread 0x9be02700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 11
******task type: 1 is handle
threadid is 0x9be02700, working on task: 1
[pool_add_worker], add task count: g_pool->cur_queue_size: 12
[pool_add_worker], add task count: g_pool->cur_queue_size: 13
thread 0x9c603700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 12
******task type: 2 is handle
threadid is 0x9c603700, working on task: 2
threadid is 0x9b601700, working on task: 0, end...
thread 0x9b601700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 11
******task type: 0 is handle
threadid is 0x9b601700, working on task: 3
threadid is 0x9be02700, working on task: 1, end...
thread 0x9be02700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 10
******task type: 1 is handle
threadid is 0x9be02700, working on task: 4
threadid is 0x9c603700, working on task: 2, end...
thread 0x9c603700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 9
******task type: 2 is handle
threadid is 0x9c603700, working on task: 5
threadid is 0x9b601700, working on task: 3, end...
thread 0x9b601700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 8
******task type: 0 is handle
threadid is 0x9b601700, working on task: 6
threadid is 0x9be02700, working on task: 4, end...
thread 0x9be02700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 7
******task type: 1 is handle
threadid is 0x9be02700, working on task: 7
threadid is 0x9c603700, working on task: 5, end...
thread 0x9c603700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 6
******task type: 2 is handle
threadid is 0x9c603700, working on task: 8
threadid is 0x9b601700, working on task: 6, end...
thread 0x9b601700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 5
******task type: 0 is handle
threadid is 0x9b601700, working on task: 9
threadid is 0x9be02700, working on task: 7, end...
thread 0x9be02700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 4
******task type: 1 is handle
threadid is 0x9be02700, working on task: 10
threadid is 0x9c603700, working on task: 8, end...
thread 0x9c603700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 3
******task type: 2 is handle
threadid is 0x9c603700, working on task: 11
threadid is 0x9b601700, working on task: 9, end...
thread 0x9b601700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 2
******task type: 0 is handle
threadid is 0x9b601700, working on task: 12
threadid is 0x9be02700, working on task: 10, end...
thread 0x9be02700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 1
******task type: 1 is handle
threadid is 0x9be02700, working on task: 13
threadid is 0x9c603700, working on task: 11, end...
thread 0x9c603700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 0
******task type: 2 is handle
threadid is 0x9c603700, working on task: 14
threadid is 0x9b601700, working on task: 12, end...
thread 0x9b601700 is waiting
threadid is 0x9be02700, working on task: 13, end...
thread 0x9be02700 is waiting
threadid is 0x9c603700, working on task: 14, end...
thread 0x9c603700 is waiting
[main], g_pool->cur_queue_size = 0
thread 0x9b601700 will exit
thread 0x9be02700 will exit
thread 0x9c603700 will exit