网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务呢?
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件), 则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。
在各个编程语言的语种中都有线程池的概念,并且很多语言中直接提供了线程池,作为程序猿直接使用就可以了,下面给大家介绍一下线程池的实现原理:
线程池的组成主要分为 3 个部分,这三部分配合工作就可以得到一个完整的线程池:
任务队列,存储需要处理的任务,由工作的线程来处理这些任务
通过线程池提供的 API 函数,将一个待处理的任务添加到任务队列,或者从任务队列中删除
已处理的任务会被从任务队列中删除
线程池的使用者,也就是调用线程池函数往任务队列中添加任务的线程就是生产者线程
工作的线程(任务队列任务的消费者) ,N个
线程池中维护了一定数量的工作线程,他们的作用是是不停的读任务队列,从里边取出任务并处理
工作的线程相当于是任务队列的消费者角色,
如果任务队列为空,工作的线程将会被阻塞 (使用条件变量 / 信号量阻塞)
如果阻塞之后有了新的任务,由生产者将阻塞解除,工作线程开始工作
管理者线程(不处理任务队列中的任务),1个
它的任务是周期性的对任务队列中的任务数量以及处于忙状态的工作线程个数进行检测
当任务过多的时候,可以适当的创建一些新的工作线程
当任务过少的时候,可以适当的销毁一些工作的线程
2、代码
threadpool.h
#ifndef \_THREADPOOL\_H
#define \_THREADPOOL\_H
#include <pthread.h>
//每次添加的线程数量
const unsigned int NUMBER = 2;
// 任务结构体
typedef struct Task
{
void (\*function)(void\* arg);
void\* arg;
}Task;
// 线程池结构体
struct ThreadPool
{
// 任务队列
Task\* taskQ;
int queueCapacity; // 容量
int queueSize; // 当前任务个数
int queueFront; // 队头 -> 取数据
int queueRear; // 队尾 -> 放数据
pthread\_t managerID; // 管理者线程ID
pthread\_t \*threadIDs; // 工作的线程ID
int minNum; // 最小线程数量
int maxNum; // 最大线程数量
int busyNum; // 忙的线程的个数
int liveNum; // 存活的线程的个数
int exitNum; // 要销毁的线程个数
pthread\_mutex\_t mutexPool; // 锁整个的线程池
pthread\_mutex\_t mutexBusy; // 锁busyNum变量
pthread\_cond\_t notFull; // 任务队列是不是满了
pthread\_cond\_t notEmpty; // 任务队列是不是空了
int shutdown; // 是不是要销毁线程池, 销毁为1, 不销毁为0
};
typedef struct ThreadPool ThreadPool;
// 创建线程池并初始化
ThreadPool \*threadPoolCreate(int min, int max, int queueSize);
// 销毁线程池
int threadPoolDestroy(ThreadPool\* pool);
// 给线程池添加任务
void threadPoolAdd(ThreadPool\* pool, void(\*func)(void\*), void\* arg);
// 获取线程池中工作的线程的个数
int threadPoolBusyNum(ThreadPool\* pool);
// 获取线程池中活着的线程的个数
int threadPoolAliveNum(ThreadPool\* pool);
//
// 工作的线程(消费者线程)任务函数
void\* worker(void\* arg);
// 管理者线程任务函数
void\* manager(void\* arg);
// 单个线程退出
void threadExit(ThreadPool\* pool);
#endif // \_THREADPOOL\_H
threadpool.c
#include "threadpool.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void taskFunc(void\* arg)
{
int num = \*(int\*)arg;
printf("thread %ld is working, number = %d\n",
pthread\_self(), num);
sleep(1);
}
int main()
{
// 创建线程池
ThreadPool\* pool = threadPoolCreate(3, 10, 100);
for (int i = 0; i < 100; ++i)
{
int\* num = (int\*)malloc(sizeof(int));
\*num = i + 100;
threadPoolAdd(pool, taskFunc, num);
}
sleep(30);
threadPoolDestroy(pool);
return 0;
}
ThreadPool\* threadPoolCreate(int min, int max, int queueSize)
{
ThreadPool\* pool = (ThreadPool\*)malloc(sizeof(ThreadPool));
do
{
if (pool == NULL)
{
printf("malloc threadpool fail...\n");
break;
}
pool->threadIDs = (pthread\_t\*)malloc(sizeof(pthread\_t) \* max);
if (pool->threadIDs == NULL)
{
printf("malloc threadIDs fail...\n");
break;
}
memset(pool->threadIDs, 0, sizeof(pthread\_t) \* max);
pool->minNum = min;
pool->maxNum = max;
pool->busyNum = 0;
pool->liveNum = min; // 和最小个数相等
pool->exitNum = 0;
if (pthread\_mutex\_init(&pool->mutexPool, NULL) != 0 ||
pthread\_mutex\_init(&pool->mutexBusy, NULL) != 0 ||
pthread\_cond\_init(&pool->notEmpty, NULL) != 0 ||
pthread\_cond\_init(&pool->notFull, NULL) != 0)
{
printf("mutex or condition init fail...\n");
break;
}
// 任务队列
pool->taskQ = (Task\*)malloc(sizeof(Task) \* queueSize);
pool->queueCapacity = queueSize;
pool->queueSize = 0;
pool->queueFront = 0;
pool->queueRear = 0;
pool->shutdown = 0;
// 创建线程
pthread\_create(&pool->managerID, NULL, manager, pool);
for (int i = 0; i < min; ++i)
{
pthread\_create(&pool->threadIDs[i], NULL, worker, pool);
}
return pool;
} while (0);
// 释放资源
if (pool && pool->threadIDs) free(pool->threadIDs);
if (pool && pool->taskQ) free(pool->taskQ);
if (pool) free(pool);
return NULL;
}
int threadPoolDestroy(ThreadPool\* pool)
{
if (pool == NULL)
{
return -1;
}
// 关闭线程池
pool->shutdown = 1;
// 阻塞回收管理者线程
pthread\_join(pool->managerID, NULL);
// 唤醒阻塞的消费者线程
for (int i = 0; i < pool->liveNum; ++i)
{
pthread\_cond\_signal(&pool->notEmpty);
}
// 释放堆内存
if (pool->taskQ)
{
free(pool->taskQ);
}
if (pool->threadIDs)
{
free(pool->threadIDs);
}
pthread\_mutex\_destroy(&pool->mutexPool);
pthread\_mutex\_destroy(&pool->mutexBusy);
pthread\_cond\_destroy(&pool->notEmpty);
pthread\_cond\_destroy(&pool->notFull);
free(pool);
pool = NULL;
return 0;
}
void threadPoolAdd(ThreadPool\* pool, void(\*func)(void\*), void\* arg)
{
pthread\_mutex\_lock(&pool->mutexPool);
while (pool->queueSize == pool->queueCapacity && !pool->shutdown)
{
// 阻塞生产者线程
pthread\_cond\_wait(&pool->notFull, &pool->mutexPool);
}
if (pool->shutdown)
{
pthread\_mutex\_unlock(&pool->mutexPool);
return;
}
// 添加任务
pool->taskQ[pool->queueRear].function = func;
pool->taskQ[pool->queueRear].arg = arg;
pool->queueRear = (pool->queueRear + 1) % pool->queueCapacity;
pool->queueSize++;
pthread\_cond\_signal(&pool->notEmpty);
pthread\_mutex\_unlock(&pool->mutexPool);
}
int threadPoolBusyNum(ThreadPool\* pool)
{
pthread\_mutex\_lock(&pool->mutexBusy);
int busyNum = pool->busyNum;
pthread\_mutex\_unlock(&pool->mutexBusy);
return busyNum;
}
int threadPoolAliveNum(ThreadPool\* pool)
{
pthread\_mutex\_lock(&pool->mutexPool);
int aliveNum = pool->liveNum;
![img](https://img-blog.csdnimg.cn/img_convert/aa49a2c95466a4acea7de10cec785528.png)
![img](https://img-blog.csdnimg.cn/img_convert/ddbd632563d01201925b5f3f87976dcf.png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
18323633)]
[外链图片转存中...(img-cRm14tqO-1715718323634)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**