一、简单线程池
/*池化技术:线程池 内存池
内存池一般用来解决,使用内存:malloc,从系统中找一块空闲的、比较大的内存,效率比较低,频繁使用malloc会使程序运行效率降低,特别是服务器。可以在程序空闲时申请一堆内存块,将申请的空闲内存块放在内存池当中,需要用时就取,用完之后又放回去,但是也不能申请过多的空闲内存块,导致内存空间不足。
线程池主要解决申请线程pthread_create的时间耗费比较大,服务器空闲的时候先创建几个线程放入线程池,但是线程创建了就会运行,为解决这个问题,可以用pthread_cond_wait()让线程睡眠,pthread_cond_signal()唤醒线程*/
/*线程通过 pthread_cond_wait(&pool->cond, &pool->mutex) 原子性地释放锁并睡眠。
其他线程可以获得锁并进入 while(pool->queueHead==pool->queueTail && pool->quit==0) 循环。
当任务队列中有任务时,通过条件变量 cond 唤醒一个线程。
被唤醒的线程重新获得锁,并执行任务队列中的任务。
执行完任务后,线程再次检查循环条件。
如果任务队列仍为空且线程池未被销毁,线程调用 pthread_cond_wait() 进入睡眠状态。
如果任务队列非空或线程池被销毁,线程继续执行下一任务或退出线程。*/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
//表示队列结点的结构体
struct Task{
void (*function)(void *arg);
void *arg;
struct Task *next;
};
//表示线程池
struct ThreadPool{
//任务队列
struct Task *queueHead;
struct Task *queueTail;
//线程的数量
int thread_num;
//线程号
pthread_t *threadID;//指向一段堆内存,内存的大小由num决定
//互斥锁 条件变量
pthread_mutex_t mutex;
pthread_cond_t cond;
//销毁线程池标志
int quit;
};
//线程处理函数
//参数就是线程池
void *thread_routine(void *arg){
struct ThreadPool* pool=(struct ThreadPool*)arg;
while(true){
//准备访问任务队列,获取锁
pthread_mutex_lock(&pool->mutex);
printf("线程 %ld 获得锁\n",pthread_self());
//如果任务队列为空并且线程池没有被销毁 线程睡眠
while(pool->queueHead==pool->queueTail&&pool->quit==0){
printf("线程 %ld 即将睡眠\n",pthread_self());
//获得条件变量 原子释放互斥锁
pthread_cond_wait(&pool->cond,&pool->mutex);
printf("线程 %ld 被唤醒,重新获得互斥锁\n",pthread_self());
}
//如果线程池被销毁
if(pool->quit==1){
//释放锁
pthread_mutex_unlock(&pool->mutex);
printf("线程池被销毁 线程%ld退出...\n",pthread_self());
pthread_exit((void *)0);
}
//从任务队列中获取一个任务,并且执行
struct Task task;
struct Task* t=pool->queueHead->next;
task.function=t->function;
task.arg=t->arg;
pool->queueHead->next=t->next;
free(t);
if(pool->queueHead->next==NULL){
pool->queueTail=pool->queueHead;
}
//释放互斥锁
pthread_mutex_unlock(&pool->mutex);
//执行任务
printf("thread %ld start work ...\n",pthread_self());
task.function(task.arg);
printf("thread %ld end work ...\n",pthread_self());
}
}
//创建线程池 在线程池中创建线程
struct ThreadPool* create_thread_pool(int thread_num){
//申请线程池结构体
struct ThreadPool* pool=(struct ThreadPool*)malloc(sizeof(struct ThreadPool));
if(NULL==pool){
fprintf(stderr,"malloc ThreadPool failure\n");
return NULL;
}
//初始化任务队列
pool->queueHead=(struct Task*)malloc(sizeof(struct Task));
if(NULL==pool->queueHead){
fprintf(stderr,"malloc Tesk failure\n");
free(pool);
return NULL;
}
pool->queueTail=pool->queueHead;
pool->queueHead->next=NULL;
//初始化线程的数量
pool->thread_num=thread_num;
//初始化线程号
pool->threadID=(pthread_t*)malloc(sizeof(pthread_t)*thread_num);
if(NULL==pool->threadID){
fprintf(stderr,"malloc pthread_t failure\n");
free(pool->queueHead);
free(pool);
return NULL;
}
//初始化线程
for(int i=0;i<thread_num;i++){
//线程创建函数 线程ID 线程属性 线程处理函数 线程池
if(pthread_create(&pool->threadID[i],NULL,thread_routine,pool)!=0){
fprintf(stderr,"pthread_create failure\n");
free(pool->queueHead);
free(pool->threadID);
free(pool);
return NULL;
}
//线程运行结束之后自动释放资源
pthread_detach(pool->threadID[i]);
}
//初始化互斥锁 信号量
//互斥锁/信号量 互斥锁/信号量属性
pthread_mutex_init(&pool->mutex,NULL);
pthread_cond_init(&pool->cond,NULL);
//初始化销毁线程池标志
pool->quit=0;
}
//任务执行函数
void task_func(void* arg){
int num=*(int*)arg;
printf("thread %ld is working num = %d ...\n",pthread_self(),num);
sleep(1);
free(arg);
}
//往任务队列中添加任务
//线程池 任务执行函数 任务执行函数参数
void thread_pool_add_task(struct ThreadPool*pool,void (*func)(void *),void *arg){
pthread_mutex_lock(&pool->mutex);
//进队操作
struct Task*t=(struct Task*)malloc(sizeof(struct Task));
if(NULL==t){//如果获取任务结构体内存失败,返回
fprintf(stderr,"malloc Task failure\n");
return;
}
t->function=func;
t->arg=arg;
t->next=NULL;
pool->queueTail->next=t;
pool->queueTail=t;
pthread_mutex_unlock(&pool->mutex);
pthread_cond_signal(&pool->cond);//唤醒线程
}
//销毁线程池
void thread_pool_destroy(struct ThreadPool*pool){
//更行销毁线程池标志
pool->quit=1;
//唤醒所有线程池
for(int i=0;i<pool->thread_num;i++){
pthread_cond_signal(&pool->cond);
}
//释放线程号
if(pool->threadID!=NULL){
free(pool->threadID);
}
//释放任务队列
while(pool->queueHead->next!=NULL){
struct Task*t=pool->queueHead->next;
pool->queueHead->next=t->next;
free(t);
}
free(pool->queueHead);//释放任务队列头指针
//销毁互斥锁 条件变量
pthread_mutex_destroy(&pool->mutex);
pthread_cond_destroy(&pool->cond);
//释放线程池结构体
free(pool);
}
int main(){
//创建线程池
struct ThreadPool* pool=create_thread_pool(10);
if(NULL==pool){//创建失败则直接返回
return -1;
}
printf("线程池创建完成\n");
sleep(1);
//主线程往任务队列中添加任务,并且唤醒线程池中的线程
for(int i=0;i<50;i++){
int *n=(int*)malloc(sizeof(int));
*n=i;
//把任务添加到任务队列
thread_pool_add_task(pool,task_func,n);
}
sleep(20);//主线程等待所有线程完成任务
thread_pool_destroy(pool);//销毁线程池
return 0;
}
二、复杂线程池
2.1 实验思想
线程池主要就是编写线程执行函数,有任务时应该要及时唤醒线程,没有任务时可以让线程睡眠,并记录线程睡眠的时间,如果线程睡眠时间太长,可以唤醒线程之后终止线程。同时,为了缓解程序经常申请和释放内存空间的压力,当有任务时,我们可以多创建几个线程,这样,下一个任务来的时候就不需要再创建线程,可以缓解高并发连接时申请和释放内存空间的压力。
其次,就是要保证线程之间要互斥访问临界资源,本次实验中,各线程应该互斥访问任务队列。
最后,在一些实际应用中,每个任务的优先级是不一样的,因此,可以为每个任务设置优先级,线程池优先为优先级高的任务分配线程。
2.2 实验
首先定义任务结构体。
其中包含任务执行函数和任务执行函数参数。
接下来,定义线程池。
接下来定义线程池执行函数,其中线程开始执行之后需要先获得锁,然会根据任务队列是否有任务和线程池是否被销毁判断线程是否需要睡眠。
线程睡眠时需要记录线程是否睡眠超时。
接下需要判断线程退出上述循环是什么原因导致其退出循环,一般有三个原因。
1)任务队列中有任务
2)线程池被销毁
3)线程睡眠超时
接下来一次判断线程超时属于哪一种情况即可。
1、任务队列中有任务
从任务队列中取一个任务,释放互斥锁之后执行任务,执行完以后重新获得互斥锁。
2、线程池被销毁
正在执行线程数做减减操作,如果没有正在执行线程,则唤醒正在睡眠的线程。
3、线程睡眠超时
正在执行线程数做减减操作,线程退出循环之后自动退出。
任务执行函数只需要输出一个数即可。
除此以外,线程池创建函数不是重点,说一下添加任务之后如何为任务分配线程。如果线程池中有睡眠线程,则唤醒睡眠线程,没有睡眠线程则创建一个新线程,这里,为了提高程序的效率,可以多创建几个,用于应对高并发请求的情况。
接下来主线程向任务队列中添加50个任务,之后主线程睡眠10s等待子线程完成任务。
除此之外,主要有需要,本实验设计的线程池最多可以创建1000个线程。
2.3 运行结果
运行结果太多,只截取其中的一部分。
线程正在工作:
线程完成工作:
线程超时等待并退出:
2.4 实验代码(Linux操作系统,C语言)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdbool.h>
//表示队列结点的结构体
struct Task{
void (*function)(void *arg);
void *arg;
struct Task *next;
};
struct ThreadPool{
//任务队列
struct Task *queueHead;
struct Task *queueTail;
//线程的数量
int max_thread_num;
int counter;
int idle;
//互斥锁 条件变量
pthread_mutex_t mutex;
pthread_mutex_t task_mutex;
pthread_cond_t cond;
//销毁线程池标志
int quit;
};
//线程池处理函数
void* thread_routine(void* arg){
struct ThreadPool* pool=(struct ThreadPool*)arg;
struct timespec abstime;//用于存储时间
int timeout;//用于标记一个线程是否超时等待
while(true){
timeout = 0;//将超时标志置为0,表示线程还未超时
//准备访问任务队列,获取锁
pthread_mutex_lock(&pool->mutex);
//printf("线程 %ld 获得锁\n",pthread_self());
pool -> idle++;//增加线程池的空闲线程数
//如果任务队列为空并且线程池没有被销毁 线程睡眠
while(pool->queueHead==pool->queueTail&&pool->quit==0){
//printf("线程 %ld 即将睡眠\n",pthread_self());
clock_gettime(CLOCK_REALTIME,&abstime);//获取当前时间
abstime.tv_sec += 2;//将超时时间设置为当前时间的后2秒
//获得条件变量 原子释放互斥锁
int status=pthread_cond_timedwait(&pool->cond,&pool->mutex,&abstime);
//printf("线程 %ld 被唤醒,重新获得互斥锁\n",pthread_self());
if(status == ETIMEDOUT)//如果线程睡眠超时
{
//打印线程睡眠超时信息
printf("thread 0x%0x is wait timed out\n",(int)pthread_self());
timeout = 1;//将线程睡眠超时标志置为1
break;//结束线程睡眠任务循环
}
}
pool->idle--;
if(pool->queueHead->next != NULL)
{
//从任务队列中获取一个任务,并且执行
struct Task task;
struct Task* t=pool->queueHead->next;
task.function=t->function;
task.arg=t->arg;
pool->queueHead->next=t->next;
free(t);
if(pool->queueHead->next==NULL){
pool->queueTail=pool->queueHead;
}
//释放互斥锁
pthread_mutex_unlock(&pool->mutex);
//执行任务
printf("thread %ld start work ...\n",pthread_self());
task.function(task.arg);
printf("thread %ld end work ...\n",pthread_self());
pthread_mutex_lock(&pool->mutex);//重新获取锁
}
//如果线程池被销毁
if(pool->quit==1 && pool->queueHead->next == NULL)
{
pool -> counter--;//减少执行线程数
if(pool->counter == 0)//如果执行线程数降为0
{
pthread_cond_signal(&pool->cond);//唤醒线程
}
pthread_mutex_unlock(&pool->mutex);//释放锁
break;
}
if(timeout==1&&pool->queueHead->next==NULL)//如果线程超时且任务队列为空
{
pool -> counter--;//减少执行线程数
pthread_mutex_unlock(&pool->mutex);//释放锁
break;
}
pthread_mutex_unlock(&pool->mutex);
}
printf("线程%ld退出...\n",pthread_self());
return NULL;
}
//创建线程池 在线程池中创建线程
struct ThreadPool* create_thread_pool(int max_thread_num){
//申请线程池结构体
struct ThreadPool* pool=(struct ThreadPool*)malloc(sizeof(struct ThreadPool));
if(NULL==pool){
fprintf(stderr,"malloc ThreadPool failure\n");
return NULL;
}
//初始化任务队列
pool->queueHead=(struct Task*)malloc(sizeof(struct Task));
if(NULL==pool->queueHead){
fprintf(stderr,"malloc Tesk failure\n");
free(pool);
return NULL;
}
pool->queueTail=pool->queueHead;
pool->queueHead->next=NULL;
//初始化线程的数量
pool->max_thread_num=max_thread_num;
pool->counter = 0;//初始化线程池中当前运行的线程数量为0
pool->idle = 0;//初始化空闲线程数量
//初始化互斥锁 信号量
//互斥锁/信号量 互斥锁/信号量属性
pthread_mutex_init(&pool->mutex,NULL);
pthread_mutex_init(&pool->task_mutex,NULL);
pthread_cond_init(&pool->cond,NULL);
//初始化销毁线程池标志
pool->quit=0;
}
//任务执行函数
void task_func(void* arg){
int num=*(int*)arg;
printf("thread %ld is working num = %d ...\n",pthread_self(),num);
sleep(1);
free(arg);
}
//往任务队列中添加任务
void thread_pool_add_task(struct ThreadPool*pool,void (*func)(void*),void* arg){
//进队操作
struct Task*t=(struct Task*)malloc(sizeof(struct Task));
if(NULL==t){//如果获取任务结构体内存失败,返回
fprintf(stderr,"malloc Task failure\n");
return;
}
t->function=func;
t->arg=arg;
t->next=NULL;
pthread_mutex_lock(&pool->mutex);
pool->queueTail->next=t;
pool->queueTail=t;
//如果有睡眠线程,则唤醒睡眠线程
if(pool->idle > 0)
{
pthread_cond_signal(&pool->cond);//唤醒线程
}
//如果没有睡眠线程,且线程池中正在运行线程数小于最大线程数,则创建一个新线程
else if(pool -> counter < pool -> max_thread_num)//可以多创建几个线程
{
int num=2;
for(int i=1;i<=num && pool->counter<pool->max_thread_num;i++){
pthread_t tid;
//pthread_create(线程号,线程属性,线程处理函数,线程池)
pthread_create(&tid,NULL,thread_routine,pool);
pthread_detach(tid);
pool->counter++;//增加线程池中正在运行线程数
}
}
pthread_mutex_unlock(&pool->mutex);
}
//销毁线程池
void thread_pool_destroy(struct ThreadPool*pool){
if(pool->quit==1){
return;
}
pthread_mutex_lock(&pool->mutex);
//更行销毁线程池标志
pool->quit=1;
if(pool->counter > 0)//如果仍有线程在运行
{
if(pool -> idle > 0){//如果有睡眠线程
//广播通知所有线程,以确保它们能够检查线程池销毁标志
pthread_cond_broadcast(&pool -> cond);
}
//等待所有正在运行的线程完成工作
while(pool -> counter > 0)
{
pthread_cond_wait(&pool -> cond,&pool -> mutex);//睡眠销毁线程(主线程)
}
}
//释放任务队列
while(pool->queueHead->next!=NULL){
struct Task*t=pool->queueHead->next;
pool->queueHead->next=t->next;
free(t);
}
free(pool->queueHead);//释放任务队列头指针
pthread_mutex_unlock(&pool->mutex);
//销毁互斥锁 条件变量
pthread_mutex_destroy(&pool->mutex);
pthread_mutex_destroy(&pool->task_mutex);
pthread_cond_destroy(&pool->cond);
//释放线程池结构体
free(pool);
}
int main(){
//创建线程池
struct ThreadPool* pool=create_thread_pool(10);
if(NULL==pool){//创建失败则直接返回
return -1;
}
printf("线程池创建完成\n");
sleep(1);
//主线程往任务队列中添加任务,并且唤醒线程池中的线程
for(int i=0;i<50;i++){
int *n=(int*)malloc(sizeof(int));
*n=i;
//把任务添加到任务队列
thread_pool_add_task(pool,task_func,(void*)n);
}
sleep(20);//主线程等待所有线程完成任务
thread_pool_destroy(pool);//销毁线程池
return 0;
}