线程池的概念:
线程池就是首先创建一些线程,它们的集合称为线程池。使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。
线程池的工作机制:
在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程。
一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。
使用线程池的原因:
多线程运行时间,系统不断的启动和关闭新线程,成本非常高,会过渡消耗系统资源,以及过渡切换线程的危险,从而可能导致系统资源的崩溃。这时,线程池就是最好的选择了。
总的来说,线程池就是系统在启动时自行创建许多线程,当我们需要线程时向系统提交申请,如果有空闲线程就会由空闲线程来执行,如果没有则等待,这样一来,我们节约了大量的创建线程和销毁线程的时间和资源。
案例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
//linux环境中多线程的头文件,非C语言标准库,编译时最后要加 -lpthread 调用动态链接库
#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;/*指向线程ID数组*/
/*线程池中允许的活动线程数目*/
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)
{
//创建线程池
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;
//任务队列也是为0
pool->cur_queue_size = 0;
pool->shutdown = 0;
//所有线程的id号
pool->threadid = (pthread_t *)malloc(max_thread_num * sizeof (pthread_t));
int i = 0;
//创建线程
for (i = 0; i < max_thread_num; i++)
{
//创建线程,参数为线程ID变量地址、线程属性、线程函数、线程参数
pthread_create(&(pool->threadid[i]), NULL, thread_routine,NULL);
}
}
/*向线程池中加入任务*/
//第一个参数是执行该任务的函数指针,第二个参数是函数参数
int pool_add_worker (void *(*process)(void *arg), void *arg)
{
/*构造一个新任务*/
CThread_worker *newworker = (CThread_worker *)malloc(sizeof(CThread_worker));
//将函数指针赋给该任务的process
newworker->process = process;
//将函数指针所需的参数赋给arg
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;
}
//断言
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->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)
{
//输出线程ID
printf("starting thread 0x%x\n", pthread_self());
while(1)
{
//加锁
pthread_mutex_lock (&(pool->queue_lock));
/*如果等待队列为0并且不销毁线程池,则处于阻塞状态; 注意
pthread_cond_wait是一个原子操作,等待前会解锁,唤醒后会加锁*/
//如果没有任务就等待
while(pool->cur_queue_size == 0 && !pool->shutdown)
{
printf("thread 0x%x is waiting\n", pthread_self ());
//等待资源,条件变量用于通知。会释放第二个参数的锁,以供添加;函数返回时重新加锁。
pthread_cond_wait(&(pool->queue_ready), &(pool->queue_lock));
}
/*线程池要销毁了*/
if (pool->shutdown)
{
/*遇到break,continue,return等跳转语句,千万不要忘记先解锁*/
pthread_mutex_unlock(&(pool->queue_lock));
printf ("thread 0x%x will exit\n", pthread_self());
pthread_exit(NULL);
}
printf("thread 0x%x 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;
}
/*这一句应该是不可达的*/
pthread_exit (NULL);
}
//测试代码
void * myprocess(void *arg)
{
printf("threadid is 0x%x, 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;
for (i = 0; i < 10; i++)
{
workingnum[i] = i;
pool_add_worker(myprocess, &workingnum[i]);
}
/*等待所有任务完成*/
sleep(5);
/*销毁线程池*/
pool_destroy();
free(workingnum);
return 0;
}