21.线程池
21.线程池
什么是线程池?
是一个抽象的概念,若干个线程组合到一起,形成线程池。
为什么需要线程池?
多线程版服务器一个客户端就需要创建一个线程!若客户端太多,显然不太合适。
什么时候需要创建线程池呢?简单的说,如果一个应用需要频繁地创建和销毁线程,而任务执行的时间又非常短,这样线程创建和销毁的带来的开销就不容忽视,这时也是线程池该出场的机会了。如果线程创建和销毁时间相比任务执行时间可以忽略不计,则没有必要使用线程池了。
实现的时候类似于生产者和消费者。
线程池和任务池:
任务池相当于共享资源,所以需要使用互斥锁,当任务池中没有任务的时候需要让线程阻塞,所以需要使用条件变量。
如何让线程执行不同的任务?
使用回到函数,在任务中设置任务执行函数,这样可以起到不同的任务执行不同的函数。
通过阅读线程池代码思考如下问题?
▶熟悉结构体 threadpool_t
▶线程池如何创建起来? 各种初始化,malloc,pthread_create,pthread_cond_init pthread_mutex_init
▶线程池内都有几类线程? 2类:管理线程+工作线程
▶管理者线程的任务是什么?任务如何实现? 任务是添加线程或者删除线程,通过2个算法,删除线程 wait_exit_thr_num = 10
▶工作线程如何工作? 等待有任务,抢到任务,修改busy_thr_num ++ 执行任务 修改 busy_thr_num --
▶线程池是如何销毁的? 自爆shutdown 诱杀!
threadpoolsimple.h
#ifndef _THREADPOOL_H
#define _THREADPOOL_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
typedef struct _PoolTask
{
int tasknum;//模拟任务编号
void *arg;//回调函数参数
void (*task_func)(void *arg);//任务的回调函数
}PoolTask ;
typedef struct _ThreadPool
{
int max_job_num;//最大任务个数
int job_num;//实际任务个数
PoolTask *tasks;//任务队列数组
int job_push;//入队位置
int job_pop;// 出队位置
int thr_num;//线程池内线程个数
pthread_t *threads;//线程池内线程数组
int shutdown;//是否关闭线程池
pthread_mutex_t pool_lock;//线程池的锁
pthread_cond_t empty_task;//任务队列为空的条件
pthread_cond_t not_empty_task;//任务队列不为空的条件
}ThreadPool;
void create_threadpool(int thrnum,int maxtasknum);//创建线程池--thrnum 代表线程个数,maxtasknum 最大任务个数
void destroy_threadpool(ThreadPool *pool);//摧毁线程池
void addtask(ThreadPool *pool);//添加任务到线程池
void taskRun(void *arg);//任务回调函数
#endif
threadpoolsimple.c
//简易版线程池
#include "threadpoolsimple.h"
ThreadPool* thrPool = NULL;
int beginnum = 1000;
void* thrRun(void* arg)
{
//printf("begin call %s-----\n",__FUNCTION__);
ThreadPool* pool = (ThreadPool*)arg;
int taskpos = 0;//任务位置
PoolTask* task = (PoolTask*)malloc(sizeof(PoolTask));
while (1)
{
//获取任务,先要尝试加锁
pthread_mutex_lock(&thrPool->pool_lock);
//无任务并且线程池不是要摧毁
while (thrPool->job_num <= 0 && !thrPool->shutdown)
{
//如果没有任务,线程会阻塞
pthread_cond_wait(&thrPool->not_empty_task, &thrPool->pool_lock);
}
if (thrPool->job_num)
{
//有任务需要处理
taskpos = (thrPool->job_pop++) % thrPool->max_job_num;
//printf("task out %d...tasknum===%d tid=%lu\n",taskpos,thrPool->tasks[taskpos].tasknum,pthread_self());
//为什么要拷贝?避免任务被修改,生产者会添加任务
memcpy(task, &thrPool->tasks[taskpos], sizeof(PoolTask));
task->arg = task;
thrPool->job_num--;
//task = &thrPool->tasks[taskpos];
pthread_cond_signal(&thrPool->empty_task);//通知生产者
}
if (thrPool->shutdown)
{
//代表要摧毁线程池,此时线程退出即可
//pthread_detach(pthread_self());//临死前分家
pthread_mutex_unlock(&thrPool->pool_lock);
free(task);
pthread_exit(NULL);
}
//释放锁
pthread_mutex_unlock(&thrPool->pool_lock);
task->task_func(task->arg);//执行回调函数
}
//printf("end call %s-----\n",__FUNCTION__);
}
//创建线程池
void create_threadpool(int thrnum, int maxtasknum)
{
printf("begin call %s-----\n", __FUNCTION__);
thrPool = (ThreadPool*)malloc(sizeof(ThreadPool));
thrPool->thr_num = thrnum;
thrPool->max_job_num = maxtasknum;
thrPool->shutdown = 0;//是否摧毁线程池,1代表摧毁
thrPool->job_push = 0;//任务队列添加的位置
thrPool->job_pop = 0;//任务队列出队的位置
thrPool->job_num = 0;//初始化的任务个数为0
thrPool->tasks = (PoolTask*)malloc((sizeof(PoolTask) * maxtasknum));//申请最大的任务队列
//初始化锁和条件变量
pthread_mutex_init(&thrPool->pool_lock, NULL);
pthread_cond_init(&thrPool->empty_task, NULL);
pthread_cond_init(&thrPool->not_empty_task, NULL);
int i = 0;
thrPool->threads = (pthread_t*)malloc(sizeof(pthread_t) * thrnum);//申请n个线程id的空间
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
for (i = 0; i < thrnum; i++)
{
pthread_create(&thrPool->threads[i], &attr, thrRun, (void*)thrPool);//创建多个线程
}
//printf("end call %s-----\n",__FUNCTION__);
}
//摧毁线程池
void destroy_threadpool(ThreadPool* pool)
{
pool->shutdown = 1;//开始自爆
pthread_cond_broadcast(&pool->not_empty_task);//诱杀
int i = 0;
for (i = 0; i < pool->thr_num; i++)
{
pthread_join(pool->threads[i], NULL);
}
pthread_cond_destroy(&pool->not_empty_task);
pthread_cond_destroy(&pool->empty_task);
pthread_mutex_destroy(&pool->pool_lock);
free(pool->tasks);
free(pool->threads);
free(pool);
}
//添加任务到线程池
void addtask(ThreadPool* pool)
{
//printf("begin call %s-----\n",__FUNCTION__);
pthread_mutex_lock(&pool->pool_lock);
//实际任务总数大于最大任务个数则阻塞等待(等待任务被处理)
while (pool->max_job_num <= pool->job_num)
{
pthread_cond_wait(&pool->empty_task, &pool->pool_lock);
}
int taskpos = (pool->job_push++) % pool->max_job_num;
//printf("add task %d tasknum===%d\n",taskpos,beginnum);
pool->tasks[taskpos].tasknum = beginnum++;
pool->tasks[taskpos].arg = (void*)&pool->tasks[taskpos];
pool->tasks[taskpos].task_func = taskRun;
pool->job_num++;
pthread_mutex_unlock(&pool->pool_lock);
pthread_cond_signal(&pool->not_empty_task);//通知包身工
//printf("end call %s-----\n",__FUNCTION__);
}
//任务回调函数
void taskRun(void* arg)
{
PoolTask* task = (PoolTask*)arg;
int num = task->tasknum;
printf("task %d is runing %lu\n", num, pthread_self());
sleep(1);
printf("task %d is done %lu\n", num, pthread_self());
}
int main()
{
create_threadpool(3, 20);
int i = 0;
for (i = 0; i < 50; i++)
{
addtask(thrPool);//模拟添加任务
}
sleep(20);
destroy_threadpool(thrPool);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)