线程池的函数分析
线程池
线程池(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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!