线程池设计

本文详细介绍了线程池的工作原理,包括线程池的创建、任务添加、销毁以及线程的管理和调度。同时,讨论了多路IO转接技术如select、poll、epoll在处理并发连接时的角色。线程池通过预先创建线程减少资源消耗,提高响应速度,而多路IO转接则专注于客户端和服务端的连接管理。两者的结合在高并发场景下能有效提升系统效率。
摘要由CSDN通过智能技术生成

IO模式:
阻塞
非阻塞忙轮询
响应式-- 多路IO转接

多路IO转接:
select、poll、epoll

//错了?
//多进程/多线程比多路IO转接慢在哪?
// 多进程或多线程的话,内核需要在多个进程或线程之间切换,消耗比较大

//======================================================================================
在一个应用程序中,会多次使用线程,因此免不了就需要多次线程的创建及销毁。
但是多次线程创建和销毁会带来较大的内存资源消耗,为了 处理这种情况,就是就提出了线程池的概念。
线程池我觉得它是一个虚拟的概念,并不是说真的有一个池子。
它是指我们在需要线程执行操作之前提前创建好一些线程,也就是线程池。

这样做的好处是:
1.降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
2.提高响应速度。 当任务到达时,任务可以不需要等到线程创建就能立即执行。
3.提高线程的可管理性。 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,
还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控

应用场景:
1.CPU密集型
2.任务比较多、比较小 并发执行大量短期的小任务

浏览器

我实现的这个简易线程池的话,里面有两个比较重要的结构体,一个是关于线程池本身的,一个是关于任务
关于任务的这个结构体比较简单,就包含一个函数指针,表示要完成的任务,还有就是这个函数的参数。
在线程池本身这个结构体中,里面包含了线程池的相关信息,
有最小、最大线程数,线程tid数组、当前存活的线程数、当前正忙的线程数、…、还有一个比较重要的是 管理线程。

线程池实现的机制:
在创建线程池的时候,我们根据最大线程数进行创建。将创建的线程都阻塞在任务队列的条件变量上
当有任务到来,我们就从阻塞在条件变量上的空闲线程中找一个去执行
同样,我们也会设置一个任务队列的最大数目,如果任务数目没有超过最大,那么新来的任务就会添加到任务队列中,
如果超过的话,就需要种颜色,等到队列中的任务被执行,留出来位置,再给新来的任务
线程池过大,会造成内存资源浪费;线程池过小,会降低程序的并发度。因此我们需要一个线程来管理线程池中的线程,
这个线程暂且叫他管理线程。
管理线程的任务是根据当前存活的线程数、当前正在忙的线程数、任务队列中实际任务数来对线程池进行添加活销毁部分线程

//======================================================================================

线程池
	如果每次来一个请求,就调用pthread_create()新建一个thread,等请求结束再调用pthread_exit()释放掉,这样会
	浪费资源,于是就想着在server和client连接之前提前创建好一堆线程,也就是线程池,这是一个虚拟的概念。
	
	当客户端发来数据的时候,就让线程池中的某个线程去处理。所以其实server端和线程池中的线程有一个共享区域。
	线程池中的线程在没有任务可以执行的时候,是阻塞在条件变量上的,pthread_cond_wait(),当有任务来的时候,
	用pthread_cond_signal() [激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个]或 
	pthread_cond_broadcast()[激活所有等待线程] 唤醒线程去处理,处理完毕后,该线程重新回到线程池中阻塞等待任务到来。
	
	打开一个火狐浏览器 用ps -aux | grep fairfox查找,发现只有一个,加入进程号为123456
	然后再 ps -Lf 123456   发现有一堆LWP(理解为线程),有一个NLWP 它显示的就是LWP的个数,这就是它使用的线程池中线程的个数
	
	多路IO转接主要负责的是客户端和服务器端的连接,而线程池负责的是服务端收到客户端的数据之后的处理
	两者是要结合
	
typedef struct {
	void *(*funtion)(void*);  //函数指针,回调函数
	void *arg;				  //回调函数的参数
}threadpool_task_t;  		  //各子线程任务结构体

//线程池相关信息
struct threadpool_t{
	pthread_mutex_t lock;				//锁住本结构体
	pthread_mutex_t thread_counter;		//记录处于忙状态线程个数的锁
	
	pthread_cond_t queue_not_full;		//当任务队列满时,添加任务的线程阻塞,等待此条件变量
	pthread_cond_t queue_not_empty;		//当任务队列不为空时,通知等待任务的线程
	
	pthread_t *threads;					//线程tid数组
	pthread_t adjust_tid;				//管理线程tid
	threadpool_task_t *task_queue;		//任务队列(数组首地址)
	
	int min_thr_num;					//线程池最小线程数目
	int max_thr_num;					//线程池最大线程数目
	int live_thr_num;					//当前存活线程个数
	int busy_thr_num;					//当前忙状态线程个数
	int wait_exit_thr_num;				//要销毁的线程个数
	
	//借助环形队列打造的任务队列,因此需要队头指针和队尾指针
	int queue_front;	//队头指针
	int queue_rear;		//队尾指针 --->  int ???
	int queue_size;		// 任务队列中实际任务数
	int queue_max_size;	//任务队列中可容纳任务上限
	
	int shutdown;		//标志位,线程池使用状态,true或者false
}

线程池模块分析:
1. main()
创建线程池
向线程池中添加任务。借助回调函数处理任务
销毁线程池
2. pthread_create();
创建线程池结构体 指针
初始化线程池结构体 变量
创建N个任务线程
创建一个管理者线程
失败时销毁开辟的所有空间
3. 子线程的回调函数 threadpool_thread()
接收参数 void *arg --> pool 结构体
加锁 --> lock : 整个结构体的锁
判断条件变量 --> wait
4. 管理者线程 adjust_thread()
接收参数 void *arg --> pool 结构体
循环10s执行一次
·上锁获得管理者需要的变量 queue_size live_thr_num busy_thr_num
·根据既定算法,使用上述3个变量,判断是否应该创建、销毁线程池中指定步长的线程
5. threadpool_add()
总功能:
模拟产生任务 num[20]
设置回调函数,处理任务 sleep(1)代表处理完成
内部实现:
加锁
初始化任务队列结构体成员:回调函数 function 和 参数 arg
利用环形队列机制,实现添加任务。 借助队尾指针挪移 % 实现
唤醒阻塞在 条件变量上的线程
解锁
6. 从3.中的wait之后继续执行,处理任务。
加锁
获取处理任务的回调函数及其参数
利用环形队列机制,实现处理任务。 借助队头指针挪移 % 实现
唤醒阻塞在条件变量上的 server
解锁
加锁
修改忙线程数目 +1
解锁
执行回调函数
加锁
修改忙线程数目 -1
解锁
7. 创建、销毁线程
管理者线程根据 task_num, live_num, busy_num
根据既定算法,使用上述3变量,判断是否应该 创建、销毁线程池中 指定步长的线程
如果满足 创建条件
pthread_creat 回调 任务线程? live_thr_num++
如果满足 销毁条件
wait_exit_thr_num = 10;
使用signal 给阻塞在条件变量上的线程发送 假条件满足信号, 跳转至pthread_cond_wait
阻塞的线程会被假信号唤醒 pthread_exit() 销毁

void *threadpool_thread(void* threadpool);
void *adjust_thread(void* threadpool);
void *threadpool_add(threadpoll_t *pool, void *(*function)(void *arg), void *arg);
void *threadpool_destory(threadpoll_t *pool);
void *threadpool_free(threadpoll_t *pool);
void *process(void* arg);

int main(void)
{
	threadpoll_t *thp = threadpoll_create(3,100,100);
	printf("poll inited");
	
	//模拟任务产生
	int num[20],i;
	for(i=0;i<20;i++)
	{
		num[i] = i;
		printf("add task %d\n",i);
		
		threadpool_add(thp,				//线程池结构体
						process,		//回调函数,表示要处理的任务
						(void*)&num[i]);
	}
	sleep(10);	//等待子线程完成任务
	threadpool_destory(thp);
}

threadpoll_t *threadpoll_create(int min_thr_num, int max_thr_num, int queue_max_size)
{
	int i=0;
	threadpool_t *pool = NULL;
	do{
		if((pool= (threadpoll_t *)malloc(sizeof(threadpoll_t)))==NULL)
		{
			printf("malloc threadpool fail!");
			break;
		}
		pool->min_thr_num = min_thr_num;
		pool->max_thr_num = max_thr_num;
		pool->busy_thr_num = 0;
		pool->live_thr_num = min_thr_num;
		pool->wait_exit_thr_num = 0;
		pool->queue_size = 0;
		pool->queue_max_size = queue_max_size;
		pool->queue_front = 0;
		pool->queue_rear = 0;
		pool->shutdown = false;		//不关闭线程池
		
		//根据最大线程上限,给工作线程数组开辟空间并清零
		pool->threads = (pthread_t*)malloc(sizeof(pthread_t)*max_thr_num);
		if(pool->threads==NULL)
		{
			printf("malloc threads fail!");
			break;
		}
		memset(pool->threads,0,sizeof((pthread_t)*max_thr_num));
		
		//给 任务队列 开辟空间		不用清零??
		pool->task_queue = (pthread_t*)malloc(sizeof(threadpool_task_t)*queue_max_size);
		if(pool->task_queue==NULL)
		{
			printf("malloc task_queue fail!");
			break;
		}
		
		//初始化互斥锁和条件变量
		if(pthread_mutex_init(&(pool->lock),NULL)!=0
			|| pthread_mutex_init(&(pool->thread_counter),NULL) !=0
			|| pthread_cond_init(&(pool->queue_not_empty),NULL) !=0
			|| pthread_cond_init(&(pool->queue_not_full),NULL) !=0)
		{
			printf("init the lock or cond fail!");
			break;
		}
		
		//启动min_thr_num个 work thread
		for(i=0;i<min_thr_num;i++){
			/*
			#include <pthread.h>
			int pthread_create(
                 pthread_t *restrict tidp,   //新创建的线程ID指向的内存单元。
                 const pthread_attr_t *restrict attr,  //线程属性,默认为NULL
                 void *(*start_rtn)(void *), //新创建的线程从start_rtn函数的地址开始运行
                 void *restrict arg //默认为NULL。若上述函数需要参数,将参数放入结构中并将地址作为arg传入。
                  );
			*/
			pthread_create(&(pool->thread[i]), NULL, threadpool_thread,(void*)pool);//会把整个线程池的属性传给每个子线程
			printf("start thread 0x%x...\n",(unsigned int)pool->threads[i]);
		}
		//创建管理者线程
		pthread_create(&(pool->adjust_tid),NULL,adjust_thread,(void*)pool);
		
		return pool;
		
	}while(0);	// do...while(0)  在这里是代替goto语句
	
	threadpoll_free(pool);	//前面代码出错,释放pool申请的空间
	return NULL;
}


//线程池中各个工作线程   不理解??
void *threadpool_thread(void* threadpool)
{
	threadpool_t *pool  = (threadpool_t*) threadpool;	//强制类型转换
	threadpool_task_t task;
	
	while(true){
		pthread_mutex_lock(&(pool->lock));
		
		while((pool->queue_size == 0)&& (!pool->shutdown)){	//满足线程池开启 但是 任务队列为空
			printf("thread 0x%x is waiting\n", (unsigned int)pthread_self());
			
			//无条件等待,要配合互斥锁一起使用
			//pthread_cond_wait作用:阻塞、mutex解锁、...(等待信号)、mutex加锁   https://blog.csdn.net/zzran/article/details/8830213
			pthread_cond_wait(&(pool->queue_not_empty), &(pool->lock));
			
			if(pool->wait_exit_thr_num > 0){
				pool->wait_exit_thr_num--;
				if(pool->live_thr_num > pool->min_thr_num){
					printf("thread 0x%x is exiting\n", (unsigned int)pthread_self());
					pool->live_thr_num--;
					pthread_mutex_unlock(&(pool->lock));
					
					pthread_exit(NULL);
				}
			}
		}
		if(pool->shutdown)
		{
			pthread_mutex_unlock(&(pool->lock));
			printf("thread 0x%x is exiting\n",(unsigned int)pthread_self());
			pthread_detach(pthread_selr\f());
			pthread_exit(NULL);
		}
		//从任务队列里获取任务,是一个出队操作
		task.function = pool->task_queue[pool->queue_rear].function;
		task.arg = pool->task_queue[pool->queue_rear].arg;
		pool->queue_front = (pool->front + 1)% pool->queue_max_size;	//队尾指针移动,模拟环形队列
		pool->queue_size--;	
		
		pthread_cond_broadcast(&(queue_not_full));
		pthread_mutex_unlock();
		
		printf("thread 0x%x is working\n",(unsigned int)pthread_self());
		pthread_mutex_lock(&(pool->thread_counter));
		pool->busy_thr_num++;
		pthread_mutex_unlock(&(pool->thread_counter));
		
		(*(task.function))(task.arg);		//执行任务回调函数
			
		printf("thread 0x%x end working\n",(unsigned int)pthread_self());
		pthread_mutex_lock(&(pool->thread_counter));
		pool->busy_thr_num--;
		pthread_mutex_unlock(&(pool->thread_counter));
	}
	pthread_exit(NULL);
}

//管理者线程
#define DEFAULT_TIME 10
void *adjust_thread(void* threadpool)
{
	threadpool_t *pool  = (threadpool_t*) threadpool;
	while(!shutdown)
	{
		sleep(DEFAULT_TIME);
		
		//取出来管理者线程需要的数据
		pthread_mutex_lock(&(pool->lock));
		int queue_size = pool->queue_size;				// 关注 任务数
		int live_thr_num = pool->live_thr_num;			// 存活线程数
		pthread_mutex_unlock(&(pool->lock));
		
		pthread_mutex_lock(&(pool->thread_counter));
		int busy_thr_num = pool->busy_thr_num;			//忙线程数
		pthread_mutex_unlock(&(pool->thread_counter));
		
		if(queue_size >= MIN_WAIT_TASK_NUM && live_thr_num < pool->max_thr_num){
			pthread_mutex_lock(&(pool->lock))
			int add = 0;
			for(int i=0;i<pool->max_thr_num && add<DEFAULT_THREAD_VARY
					&& pool->live_thr_num<pool->max_thr_num;i++){
				if(pool->threads[i] ==0 || !is_thread_alive(pool->threads[i])){
					pthread_creat
				}
			}
			
		}
	}
}


void *threadpool_add(threadpoll_t *pool, void *(*function)(void *arg), void *arg)
{
	pthread_mutex_lock(pool->lock);
	
	while(pool->queue_size == pool->queue_max_size  && (!pool->shutdown))
	{
		pthread_cond_wait(&(pool->queue_not_full),&(pool->lock));
	}
	if(pool->shutdown)
	{
		// 如果发现线程池关闭,则唤醒所有正因任务队列为空的线程
		pthread_cond_broadcast(&(pool->queue_not_empty));
		pthread_mutex_unlock(&(pool->lock));
		return 0;
	}
	
	if(pool->task_queue[pool->queue_rear].arg != NULL)
		pool->task_queue[pool->queue_rear].arg = NULL;
	
	pool->task_queue[pool->queue_rear].function = function;
	pool->task_queue[pool->queue_rear].arg = arg;
	pool->queue_rear = (pool->rear + 1)% pool->queue_max_size;	//队尾指针移动,模拟环形队列
	pool->queue_size++;		//向任务队列中添加一个任务,因此实际任务个数+1
	
	pthread_cond_signal(&(pool->queue_not_empty));
	pthread_mutex_unlock(&(pool->lock));
	
	return 0;
}

void *threadpool_destory(threadpoll_t *pool)
{
	if(pool == NULL)
		return -1;
	pool->shutdown = true;
	//先销毁管理者线程
	pthread_join(pool->adjust_tid,NULL);	//阻塞主线程等待子线程退出
	for(int i=0;i<pool->live_thr_num;i++){
		pthread_cond_broadcast(&(pool->queue_not_empty));	//通知所有的空闲线程
	}
	for(int i=0;i<pool->live_thr_num;i++){
		pthread_join(pool->threads[i],NULL);
	}
	threadpoll_free(pool);
	
	return 0;
}

/* 线程池中的线程,模拟业务处理 */
void *process(void *arg)
{
	printf("thread 0x%x working on task %d\n", (unsigned int)pthread_self(),(int)arg);
	sleep(1);  //要处理的任务,这里写sleep模拟小写转大写的具体操作    所以这里的arg参数应该为客户端的文件描述符
	printf("task %d id end\n", (int)arg);
	return NULL;
}

void *threadpool_free(threadpoll_t *pool)
{
	if(pool==NULL)
		return -1;
	if(pool->task_queue)
		free(pool->task_queue);
	if(pool->threads){
		free(pool->threads);
		pthread_mutex_lock(&(pool->lock));
		pthread_mutex_destory(&(pool->lock));
		pthread_mutex_lock(&(pool->thread_counter));
		pthread_mutex_destory(&(pool->thread_counter));
		pthread_cond_destory(&(pool->queue_not_full));
		pthread_cond_destory(&(pool->queue_not_empty));
	}
	free(pool);
	pool=NULL;
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值