线程池的函数分析

线程池

线程池(Thread Pool)是一种设计模式,用于限制计算机上同时运行的线程数量,以优化资源使用并提高性能。在多线程编程中,线程的创建和销毁是开销很大的操作,而线程池通过复用线程来减少这种开销。



线程池的函数分析


头文件thread_pool.h


#ifndef _THREAD_POOL_H_
#define _THREAD_POOL_H_

#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <pthread.h>

#define MAX_WAITING_TASKS	1000   //处于等待状态的线程数量最大为1000
#define MAX_ACTIVE_THREADS	20     //活跃的线程数量 

//任务结点  单向链表的节点,类型
struct task
{
	void *(*do_task)(void *arg); //任务函数指针  指向线程要执行的任务  格式是固定的
	void *arg;					 //需要传递给任务的参数,如果不需要,则NULL

	struct task *next;			 //指向下一个任务结点的指针
};


//线程池的管理结构体
typedef struct thread_pool
{
	pthread_mutex_t lock;		// 互斥锁
    
	pthread_cond_t  cond;		// 条件量

	bool shutdown;				//是否需要销毁线程池

	struct task *task_list;		//用于存储任务的链表

	pthread_t *tids;			//用于记录线程池中线程的ID

	unsigned max_waiting_tasks;	//线程池中线程的数量最大值
	unsigned waiting_tasks;		//处于等待状态的线程数量
	unsigned active_threads;	//正在活跃的线程数量
}thread_pool;


//初始化线程池
bool init_pool(thread_pool *pool, unsigned int threads_number);
//theards_number:为初始化线程池中添加线程的个数

//向线程池中添加任务
bool add_task(thread_pool *pool, void *(*do_task)(void *arg), void *arg);
//do_task:任务函数指针
//arg:为任务函数提供所需数据的指针

//先线程池中添加线程
int  add_thread(thread_pool *pool, unsigned int additional_threads_number);
//additional_threads:新加入线程池线程个数

//从线程池中删除线程
int  remove_thread(thread_pool *pool, unsigned int removing_threads_number);
//removing_threads:删除线程池中线程的个数

//销毁线程池
bool destroy_pool(thread_pool *pool);

//任务函数
void *routine(void *arg);
//arg:为任务函数提供所需数据的指针


#endif


主要函数集thread_pool.c


#include "thread_pool.h"

//打印当前线程ID
void handler(void *arg)
{
	//输出线程ID
	printf("[%u] is ended.\n",
		(unsigned)pthread_self());

	//释放互斥锁
	pthread_mutex_unlock((pthread_mutex_t *)arg);
}


//线程要执行的任务
void *routine(void *arg)
{
	//调试
	#ifdef DEBUG
	printf("[%u] is started.\n",
		(unsigned)pthread_self());
	#endif

	//把需要传递给线程任务的参数进行备份
	thread_pool *pool = (thread_pool *)arg;
	struct task *p;

	while(1)
	{
		//================================================//
		pthread_cleanup_push(handler, (void *)&pool->lock);   //执行打印IP任务
		pthread_mutex_lock(&pool->lock);
		//================================================//

		// 1、判断条件:没有任务和线程池状态为开启,等待条件变量唤醒
		while(pool->waiting_tasks == 0 && !pool->shutdown)
		{
			pthread_cond_wait(&pool->cond, &pool->lock);
		}

		// 2、判断条件:没有任务并且线程池为关闭状态,退出线程
		if(pool->waiting_tasks == 0 && pool->shutdown == true)
		{
			pthread_mutex_unlock(&pool->lock);
			pthread_exit(NULL); // CANNOT use 'break';
		}

		// 3、满足以上两个条件,说明有任务,线程从任务列表中取出一个任务并更新任务列表
		p = pool->task_list->next;
		pool->task_list->next = p->next;
		pool->waiting_tasks--;

		//================================================//
		pthread_mutex_unlock(&pool->lock);
		pthread_cleanup_pop(0);
		//================================================//

		//执行从任务链表取出的任务
		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);  //禁用线程取消,保护执行中的任务
		(p->do_task)(p->arg);
		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);   //开启线程取消,结束保护

		//释放完成的任务内存
		free(p);
	}

	pthread_exit(NULL);
}

//初始化线程池
bool init_pool(thread_pool *pool, unsigned int threads_number)
{
	//初始化互斥锁
	pthread_mutex_init(&pool->lock, NULL);

	//初始化条件量
	pthread_cond_init(&pool->cond, NULL);

	//销毁标志 
	pool->shutdown = false; //不销毁

	//给任务链表的节点申请堆内存
	pool->task_list = malloc(sizeof(struct task)); 

	//申请堆内存,用于存储创建出来的线程的ID,下标获取指定线程ID,最大存在20个活跃线程在线程池中
	pool->tids = malloc(sizeof(pthread_t) * MAX_ACTIVE_THREADS);

	//错误处理,对malloc进行错误处理
	if(pool->task_list == NULL || pool->tids == NULL)
	{
		perror("allocate memory error");
		return false;
	}

	//对任务链表中的节点的指针域进行初始化
	pool->task_list->next = NULL;

	//设置线程池中线程数量的最大值
	pool->max_waiting_tasks = MAX_WAITING_TASKS;
	
	//设置等待线程处理的任务的数量为0,说明现在没有任务
	pool->waiting_tasks = 0;

	//设置线程池中活跃的线程的数量
	pool->active_threads = threads_number;

	int i;

	//循环创建活跃线程
	for(i=0; i<pool->active_threads; i++)
	{
		//创建线程  把线程的ID存储在申请的堆内存
		if(pthread_create(&((pool->tids)[i]), NULL,
					routine, (void *)pool) != 0)
		{
			perror("create threads error");
			return false;
		}

		//用于调试
		#ifdef DEBUG
		printf("[%u]:[%s] ==> tids[%d]: [%u] is created.\n",
			(unsigned)pthread_self(), __FUNCTION__,
			i, (unsigned)pool->tids[i]);
		#endif
	}

	return true;
}

//先线程池的任务链表中添加任务
bool add_task(thread_pool *pool,
	      	void *(*do_task)(void *arg), void *arg)
{
	//给任务链表节点申请堆内存
	struct task *new_task = malloc(sizeof(struct task));
	if(new_task == NULL)
	{
		perror("allocate memory error");
		return false;
	}

	//赋值任务链表节点的任务函数、函数所需参数
	new_task->do_task = do_task;
	new_task->arg = arg;			
	new_task->next = NULL;			//指针域设置为NULL

	//============ LOCK =============//
	pthread_mutex_lock(&pool->lock);
	//===============================//

	//说明要处理的任务的数量大于能处理的任务数量
	if(pool->waiting_tasks >= MAX_WAITING_TASKS)
	{
		pthread_mutex_unlock(&pool->lock);

		fprintf(stderr, "too many tasks.\n");
		free(new_task);

		return false;
	}
	
	struct task *tmp = pool->task_list;

	//遍历链表,找到单向链表的尾节点
	while(tmp->next != NULL)
		tmp = tmp->next;

	//把新的要处理的任务插入到链表的尾部  尾插
	tmp->next = new_task;

	//要处理的任务的数量+1
	pool->waiting_tasks++;

	//=========== UNLOCK ============//
	pthread_mutex_unlock(&pool->lock);
	//===============================//

	//调试
	#ifdef DEBUG
	printf("[%u][%s] ==> a new task has been added.\n",
		(unsigned)pthread_self(), __FUNCTION__);
	#endif

	//唤醒第一个处于阻塞队列中的线程
	pthread_cond_signal(&pool->cond);
	return true;
}

//向线程池加入新线程,返回创建成功新线程个数
int add_thread(thread_pool *pool, unsigned additional_threads)
{
	//判断需要添加的新线程的数量是否为0
	if(additional_threads == 0)
		return 0;

	//计算线程池中总线程的数量
	unsigned total_threads =
			pool->active_threads + additional_threads;

	int i, actual_increment = 0;

	//为线程池创建新的线程,并限制创建线程个数小于等于设置的最大线程个数
	for(i = pool->active_threads;i < total_threads && i < MAX_ACTIVE_THREADS;i++)
	{
		//创建新线程
		if(pthread_create(&((pool->tids)[i]),
					NULL, routine, (void *)pool) != 0)
		{
			perror("add threads error");

			//判断是否创建任意一个新线程,如果成功创建新线程则退出循环并更新线程个数
			if(actual_increment == 0)
				return -1;

			break;
		}
		//成功创建个数+1
		actual_increment++; 

		#ifdef DEBUG
		printf("[%u]:[%s] ==> tids[%d]: [%u] is created.\n",
			(unsigned)pthread_self(), __FUNCTION__,
			i, (unsigned)pool->tids[i]);
		#endif
	}

	//更新此时线程池中活跃线程的总数
	pool->active_threads += actual_increment;
	return actual_increment;
}

//删除线程池中的部分线程
int remove_thread(thread_pool *pool, unsigned int removing_threads)
{
	//判断删除线程个数的参数是否为0
	if(removing_threads == 0)
		return pool->active_threads;

	//判断所需删除线程个数是否大于线程池中实际线程,线程池至少保留一个子线程
	int remaining_threads = pool->active_threads - removing_threads;
	remaining_threads = remaining_threads > 0 ? remaining_threads : 1;

	//循环删除线程池中线程
	int i;
	for(i=pool->active_threads-1; i>remaining_threads-1; i--)
	{
		errno = pthread_cancel(pool->tids[i]);

		if(errno != 0)
			break;

		#ifdef DEBUG
		printf("[%u]:[%s] ==> cancelling tids[%d]: [%u]...\n",
			(unsigned)pthread_self(), __FUNCTION__,
			i, (unsigned)pool->tids[i]);
		#endif
	}

	//判断是否删除成功,若成功更新线程池中线程个数
	if(i == pool->active_threads-1)
		return -1;
	else
	{
		pool->active_threads = i+1;
		return i+1;
	}
}

bool destroy_pool(thread_pool *pool)
{
	//修改线程池摧毁开关,并唤醒线程池中所有的线程
	pool->shutdown = true;
	pthread_cond_broadcast(&pool->cond);

	//循环回收线程池中所有线程的资源 
	int i;
	for(i=0; i<pool->active_threads; i++)
	{
		errno = pthread_join(pool->tids[i], NULL);
		if(errno != 0)
		{
			printf("join tids[%d] error: %s\n",
					i, strerror(errno));
		}
		else
			printf("[%u] is joined\n", (unsigned)pool->tids[i]);
		
	}

	//释放任务链表、存储线程ID数组、线程池管理结构体空间
	free(pool->task_list);
	free(pool->tids);
	free(pool);

	return true;
}


线程池简单应用main.c


#include "thread_pool.h"

//线程池测试任务函数
void *mytask(void *arg)     
{
	int n = (int)arg;

	printf("[%u][%s] ==> job will be done in %d sec...\n",
		(unsigned)pthread_self(), __FUNCTION__, n);

	sleep(n);

	printf("[%u][%s] ==> job done!\n",
		(unsigned)pthread_self(), __FUNCTION__);

	return NULL;
}

//进程开始启动及计时任务
void *count_time(void *arg)         
{
	int i = 0;
	while(1)
	{
		sleep(1);
		printf("sec: %d\n", ++i);
	}
}


int main(void)
{
	//1、创建子线程执行计时任务
	pthread_t a;
	pthread_create(&a, NULL, count_time, NULL);    

	// 2、为线程池申请堆空间并初始化线程池,为线程池添加两个线程
	thread_pool *pool = malloc(sizeof(thread_pool)); 
	init_pool(pool, 2);

	// 3、为线程池添加三个任务
	printf("throwing 3 tasks...\n");
	add_task(pool, mytask, (void *)(rand()%10));
	add_task(pool, mytask, (void *)(rand()%10));
	add_task(pool, mytask, (void *)(rand()%10));

	// 4、查看当前活跃线程
	printf("current thread number: %d\n",
			remove_thread(pool, 0));
	sleep(9);

	// 5、为线程池添加两个任务
	printf("throwing another 2 tasks...\n");
	add_task(pool, mytask, (void *)(rand()%10));
	add_task(pool, mytask, (void *)(rand()%10));

	// 6、添加两个线程
	add_thread(pool, 2);

	sleep(5);

	// 7、从线程池移除三个线程
	printf("remove 3 threads from the pool, "
	       "current thread number: %d\n",
			remove_thread(pool, 3));

	// 8、摧毁线程池
	destroy_pool(pool);
	return 0;
}

posted @   ColorsLG  阅读(17)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示