手写线程池——C和C++版本
一. C语言版
大致思路
- 采用
生产者——消费者
模型:- 生产者:用户向任务队列添加任务,是生产者。
- 消费者:线程池里面的线程从任务队列中取出任务是,是消费者。
- 任务队列:
- 单个任务结构:使用结构体封装,其中包含一个函数指针,用于指向要处理的具体任务函数。另一个是参数指针,指向向任务函数传递的参数。
- 任务队列结构:使用环形队列,从队列头取数据,从队列尾存放数据。
- 线程池结构:
- 管理者线程:用于定时检测线程池状态,根据任务数量和存货线程数量决定创建或销毁线程。
- 消费者线程:使用数组结构。承载所有用于工作的子线程。
- 任务队列:用于存储任务,尾部存,头部取。
- 锁和条件变量:用于维持线程同步。
- 线程池销毁标志:用于标志线程池状态,同时也间接参与线程的销毁。
- 其它变量:如任务队列容量、最多同时存在的线程数等。
头文件
// 线程池头文件
#ifndef _THREADPOOL_H
#define _THREADPOOL_H
typedef struct ThreadPool ThreadPool; // 使用前的预定义,具体定义在源代码内
// 创建线程池并初始化
ThreadPool *threadPoolCreate(int min, int max, int queueCapacity);
// 销毁线程池
void threadPoolDestroy(ThreadPool *pool);
// 添加工作
void threadPoolAdd(ThreadPool *pool, void (*func)(void *), void *arg);
// 获取当前正在工作的线程数
int threadPoolBusyNum(ThreadPool *pool);
// 获取当前存活的线程数量
int threadPoolAliveNum(ThreadPool *pool);
// 线程池工作函数
void* work(void *arg);
// 管理线程的工作函数
void* manage(void *arg);
// 退出当前线程,并将其在线程池内的ID值转化为0
void threadExit(ThreadPool *pool);
#endif
源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h> // C语言线程池头文件
#include "threadpool.h"
// 任务结构体
typedef struct Task{
void (*function) (void *); // 函数指针
void *arg;
}Task;
// 每次新增、删除线程的最大数量
const int NUMBER = 2;
// 线程池结构体
typedef struct ThreadPool{
// 任务队列
Task* taskQ;
int queueCapacity; // 容量
int queueSize; // 当前队列内存在的任务
int queueFront; // 队头。用于取出数据
int queueRear; // 队尾。用于存放数据
// 线程
pthread_t manageID; // 管理者线程的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 full; // 任务队列是否已经满了
pthread_cond_t empty; // 任务队列是不是空的
int shutdown; // 是否销毁线程池
}ThreadPool;
// 退出线程的函数
void threadExit(ThreadPool *pool) {
pthread_t nowThread = pthread_self();
for (int i = 0; i < pool->maxNum; i++){
if(pool->threadIDs[i] == nowThread) {
pool->threadIDs[i] = 0;
printf("consumer thread %ld is finished\n", nowThread);
pthread_exit(NULL);
break;
}
}
return ;
}
// 管理线程的函数:主要负责新增线程、销毁线程
void *manage(void *arg){
ThreadPool *pool = (ThreadPool *)arg;
while (!pool->shutdown) { // 当线程池没有被关闭时
// 三秒检测一次线程池
sleep(3);
// 访问只有读操作的内存中存储的参数,无需使用锁
int maxNum = pool->maxNum; // 最大线程数
int minNum = pool->minNum; // 最小线程数
int queueCapacity = pool->queueCapacity; // 任务队列容量
// 对临界资源进行操作
pthread_mutex_lock(&pool->mutexPool);
int liveNum = pool->liveNum; // 当前存活的线程数
int queueSize = pool->queueSize; // 当前任务数量
// 检查是否需要新增线程:当任务数量>存活线程数 && 存活线程数<最大线程数
if(queueSize > liveNum && liveNum < maxNum) {
int var = 0;
// 遍历线程数组,找出目前还未初始化的位置
for (int i = 0; i < maxNum; i++) {
if (pool->threadIDs[i] == 0){
pthread_t newThread;
pthread_create(&newThread, NULL, work, pool);
pool->threadIDs[i] = newThread;
pool->liveNum++;
if(++var >= NUMBER){
break;
}
}
}
}
// 检查是否需要销毁线程:当任务数量*2<存活线程数 && 存活线程数>最小线程数
if (queueSize * 2 < liveNum && liveNum > minNum) {
int var = 0;
// 通过设置exitNum并且释放至少一个被empty阻塞的子线程,让子线程自己死亡
for (int i = 0; i < NUMBER; i++) {
pool->exitNum++;
pthread_cond_signal(&pool->empty);
var++;
if (liveNum - var <= minNum) {
break;
}
}
}
// 释放线程池锁
pthread_mutex_unlock(&pool->mutexPool);
}
return NULL;
}
// 消费者线程的工作函数
void *work(void *arg) {
ThreadPool *pool = (ThreadPool *)arg;
while (1) {
// 需要对线程池的绝大部分内存进行操作,因此直接锁住整个线程池
pthread_mutex_lock(&pool->mutexPool);
// 如果当前任务队列为空且线程池还未被销毁,那么阻塞所有线程
while( pool->queueSize == 0 && !pool->shutdown ){
pthread_cond_wait(&pool->empty, &pool->mutexPool);
// 判断是否需要销毁子线程
if(pool->exitNum > 0){
pool->exitNum--;
if (pool->liveNum > pool->minNum) {
pool->liveNum--;
pthread_mutex_unlock(&pool->mutexPool);
threadExit(pool);
}
}
}
// 如果此时线程池已经被摧毁,那么释放锁,退出线程
if(pool->shutdown){
pthread_mutex_unlock(&pool->mutexPool);
printf("consumer thread %ld is destroyed\n", pthread_self());
pthread_exit(NULL);
}
// 此时线程走通了,那么从队列的头部取出任务并执行
Task *task = &pool->taskQ[pool->queueFront];
pool->queueFront = (++pool->queueFront) % pool->queueCapacity; // 维护一个环形数组
pool->queueSize--;
// 唤醒因为任务队列已满而被阻塞的生产者
pthread_cond_signal(&pool->full);
// 此时就可以释放整个线程池的锁。将线程工作的内容放在整体锁的外面,提升效率
pthread_mutex_unlock(&pool->mutexPool);
// 线程开始工作,修改正在工作的线程的数量
pthread_mutex_lock(&pool->mutexBusy);
pool->busyNum++;
pthread_mutex_unlock(&pool->mutexBusy);
printf("thread %ld begin work\n", pthread_self());
task->function(task->arg); // 通过指针函数执行任务
printf("thread %ld finish work\n", pthread_self());
// 线程工作完毕
free(task->arg); // 任务参数建议使用堆内存,避免了生命周期的问题
task->arg = NULL;
pthread_mutex_lock(&pool->mutexBusy);
pool->busyNum--; // 工作线程数--
pthread_mutex_unlock(&pool->mutexBusy);
}
return NULL;
}
// 初始化线程池
ThreadPool * threadPoolCreate(int min, int max, int queueCapacity){
ThreadPool *threadpool = NULL;
do {
// 初始化线程池
threadpool = (ThreadPool *)malloc(sizeof(ThreadPool));
if(threadpool == NULL){
printf("malloc threadpool fail...\n");
break;
}
// 初始化生产者线程数组L:这里使用calloc,是为了将所有线程ID初始化为0,便于后续判断
threadpool->threadIDs= (pthread_t *)calloc(max, sizeof(pthread_t));
if(threadpool->threadIDs == NULL){
printf("malloc threadIDs fail...\n");
break;
}
// 初始化变量
threadpool->minNum = min;
threadpool->maxNum = max;
threadpool->liveNum = min;
threadpool->busyNum = 0;
threadpool->exitNum = 0;
// 调用函数初始化互斥锁和条件变量
if((pthread_mutex_init(&threadpool->mutexPool, NULL) != 0)||
(pthread_mutex_init(&threadpool->mutexBusy, NULL) != 0) ||
(pthread_cond_init(&threadpool->empty, NULL) != 0) ||
(pthread_cond_init(&threadpool->full, NULL) != 0)){
printf("mutex or condition init fail...\n");
break;
}
// 对任务队列进行初始化
threadpool->taskQ = (Task *)malloc(sizeof(Task) * queueCapacity);
threadpool->queueCapacity = queueCapacity;
threadpool->queueSize = 0;
threadpool->queueFront = 0;
threadpool->queueRear = 0;
// 创建管理者线程
pthread_create(&threadpool->manageID, NULL, manage, threadpool);
// 创建消费者线程
for (int i = 0; i < min; ++i) {
pthread_create(&threadpool->threadIDs[i], NULL, work, threadpool);
}
threadpool->shutdown = 0;
printf("threadpool is successfully created!\n");
return threadpool;
}while(0); // 通过while,从而使用break,避免在if内进行多次内存释放
if(!threadpool && !threadpool->taskQ){
free(threadpool->taskQ);
}
if(!threadpool && threadpool->threadIDs){
free(threadpool->threadIDs);
}
if(threadpool){
free(threadpool);
}
return threadpool;
}
// 生产者函数:添加任务
void threadPoolAdd(ThreadPool *pool, void (*func)(void *), void *arg){
// 首先判读用户传入的pool是否已经初始化
if(pool == NULL){
return;
}
// 抢线程池锁
pthread_mutex_lock(&pool->mutexPool);
// 判断任务队列是否已经满了
while (pool->queueSize == pool->queueCapacity && !pool->shutdown){
pthread_cond_wait(&pool->full, &pool->mutexPool);
}
if(pool->shutdown){
pthread_mutex_unlock(&pool->mutexPool);
return;
}
// 在任务队列的队尾添加任务
pool->taskQ[pool->queueRear].function = func;
pool->taskQ[pool->queueRear].arg = arg;
pool->queueSize++;
pool->queueRear = (++pool->queueRear) % pool->queueCapacity;
// 唤醒被empty阻塞的消费者线程
pthread_cond_signal(&pool->empty);
// 释放线程池锁
pthread_mutex_unlock(&pool->mutexPool);
return;
}
// 销毁线程池
void threadPoolDestroy(ThreadPool *pool){
if (pool == NULL) {
return;
}
// 如果任务还没有完成,返回提示
if(pool->queueSize > 0){
printf("there are still unfinished tasks!\n");
}
// 修改删除标志符,修改这个标识符后,子线程函数内判断,并自动结束,管理线程这里要手动回收
pool->shutdown = 1;
// 唤醒阻塞的消费者线程,让其自动死亡
for(int i = 0; i < pool->liveNum; ++i) {
pthread_cond_signal(&pool->empty);
}
// 手动回收管理线程
printf("manage thread %ld is destroyed\n", pool->manageID);
pthread_join(pool->manageID, NULL);
// 释放消费者线程占用的内存
if(pool->threadIDs) {
printf("consumer thread memory is freed\n");
free(pool->threadIDs);
}
// 释放任务占用的内存
if(pool->taskQ) {
printf("task queue memory is freed\n");
free(pool->taskQ);
}
// 释放互斥锁和条件变量
printf("mutexex and condtion variables are destroyed\n");
pthread_mutex_destroy(&pool->mutexBusy);
pthread_mutex_destroy(&pool->mutexPool);
pthread_cond_destroy(&pool->empty);
pthread_cond_destroy(&pool->full);
// 释放线程池占用的内存
printf("threadpool memory is freed\n");
free(pool);
return;
}
// 获取当前存活的线程数量
int threadPoolAliveNum(ThreadPool *pool){
if (pool == NULL) {
return -1;
}
pthread_mutex_lock(&pool->mutexPool);
int liveNum = pool->liveNum;
pthread_mutex_unlock(&pool->mutexPool);
return liveNum;
}
// 获取当前正在工作的线程数量
int threadPoolBusyNum(ThreadPool *pool){
if (pool == NULL) {
return -1;
}
pthread_mutex_lock(&pool->mutexBusy);
int busyNum = pool->busyNum;
pthread_mutex_unlock(&pool->mutexBusy);
return busyNum;
}
二. C++版
项目介绍
本项目是一个基于C++11的简单线程池。
1. 项目结构
.
├── include 头文件目录
│ ├── taskqueue.h 任务队列头文件
│ └── threadpool.h 线程池头文件
├── main.cpp 测试程序
├── taskqueue.cpp 任务队列实现
└── threadpool.cpp 线程池实现
2. 编译运行
使用g++9.4.0
-
编译:
g++ main.cpp taskqueue.cpp threadpool.cpp -I include -l pthread -o a
-
运行:
./a
3. 设计思路
3.1 任务队列
作用:用来存放线程要执行的任务。
设计思路:
-
将线程的任务封装为结构体,分为:
任务函数
+参数
两部分using func_ptr = void(*)(void*); struct Task{ func_ptr callback; // 任务函数 void* arg; // 任务函数的参数 };
-
为了方便,任务队列使用
queue
。std::queue<Task> task_queue;
-
因为有多个线程要访问任务队列,为了线程安全,需要一个锁:
std::mutex mutex;
3.2 线程池
设计思路:
首先,为了保证基础功能,线程池应当包含以下变量:
- 任务队列:用来装用户的任务。
- 消费者线程:用来解决任务队列中的任务。显然会有多个。
- 管理者线程:需要一个线程来管理消费者线程和任务队列。一个就够了。具体负责:
- 新增/杀死消费者线程。
- 回收消费者线程。(
join()
)
其次,为了便于用于查看线程池状态,还应包含以下变量:
- 最大/小消费者线程数量。
- 当前存活的消费者线程数量。
- 当前忙的消费者线程数量。(当消费者线程正在处理任务队列中的任务时,该线程忙)
然后,如果存活的消费者线程过多但任务过少,一部分消费者线程会被阻塞。这时需要杀死一些消费者线程,为此,我们需要一个变量作为标志,管理者线程设置该标志后,唤醒一定数量的被阻塞的消费者线程,这些线程看到这个标志会“自尽”。因此应有变量:
- 当前要杀死的消费者线程数量。
最后,为了线程同步,我们需要锁和条件变量:
- 当前存活的消费者线程数量锁。
- 当前忙的消费者线程数量锁。
- 当前要杀死的消费者线程数量。
- 任务队列是否为空的条件变量。
(这里有三把锁,后面在使用时要注意顺序,避免死锁的出现。)
线程回收问题:
和C语言不同,C++中,凡是处于joinable
状态的线程(也就是使用含参构造器构造的),必须显式使用join()
回收或detach()
分离。因此我们不能像在C那样,直接在消费者线程中使用pthread_exit(NULL)
结束线程,而是要在消费者线程中使用return
结束任务函数,并在管理者线程中使用join()
回收该线程,随后我们才可以利用这片内存放其余的线程。
问题来了?管理者线程怎么是哪个线程需要回收的?我们可以使用为线程加一个状态码,使用结构体进行封装:
struct MyThread{
char status;
std::thread thread;
};
status
:- 0:默认状态,此时该线程的
joinable()
为假,不可以进行join()
/detach()
。有两种情况是这种状态:- 无参构造:使用无参构造器创建的线程,该线程没有任务。
- 已经被
join()
/detach()
过的线程。
- 1:一般状态。此时该线程的
joinable()
为真,且该线程正在正常的执行自己的任务函数。 - 2:待回收状态。此时该线程的
joinable()
为真,说明需要管理者线程对该线程进行回收。
- 0:默认状态,此时该线程的
有了以上状态码,我们可以让管理者线程每3秒循环一次,期间进行如下操作:
- 查看是否需要新增消费者线程。
- 查看是否需要杀死消费者线程。
- 查看是否有需要回收的消费者线程。
项目代码
1. 任务队列
1.1 头文件
#ifndef _TASKQUEUE_H
#define _TASKQUEUE_H
#include <queue>
#include <mutex>
// 定义函数指针
using func_ptr = void (*) (void*);
// 定义任务结构体
struct Task{
func_ptr callback; // 任务函数
void* arg; // 任务函数的参数
};
// 定义任务队列
class TaskQueue{
private:
std::queue<Task> task_queue;
std::mutex mutex;
public:
TaskQueue();
~TaskQueue();
void add(Task);
Task get();
// 当前任务数量(使用内连函数,提高运行效率,避免压栈)
inline int size(){
return task_queue.size();
}
};
#endif
1.2 源文件
#include "taskqueue.h"
TaskQueue::TaskQueue(){}
TaskQueue::~TaskQueue(){}
void TaskQueue::add(Task task){
this->mutex.lock();
task_queue.push(task);
this->mutex.unlock();
}
Task TaskQueue::get(){
Task t;
this->mutex.lock();
if(!task_queue.empty()){
t = task_queue.front();
task_queue.pop();
this->mutex.unlock();
return t;
}
return t;
}
2. 线程池
2.1 头文件
#ifndef _THREADPOOL_H
#define _THREADPOOL_H
#include <queue>
#include <thread>
#include <condition_variable>
#include "taskqueue.h"
// 线程结构体
struct MyThread{
// 线程状态:0:无参线程,或已经被回收的线程(状态是unjoinable),是默认状态;1:正常;2:需要回收
char status;
std::thread thread; // 线程本身
};
// 线程池
class MyThreadPool{
private:
TaskQueue* task_queue; // 任务队列
std::thread manager; // 管理者线程,定期监控工作线程和任务队列
MyThread* consumer; // 消费者。是参与工作的线程
int min_num; // 最小线程数量
int max_num; // 最大线程数量
int live_num; // 存活的线程数量
int busy_num; // 正在工作的线程的数量
int exit_num; // 每次杀死的线程数量
std::mutex live_mutex; // 锁住"存活的线程数量"这个变量
std::mutex busy_mutex; // 锁住"忙的线程数量"这个变量
std::mutex exit_mutex; // 锁住"杀死的线程数量"这个变量
std::condition_variable_any empty; // 任务队列是否为空,控制消费者
bool destory; // 是否销毁线程池
// 管理者线程任务函数
static void* manage_work(void* arg);
// 消费者线程任务函数
static void* consumer_work(void* arg);
// 销毁一个线程
static void thread_exit(void* arg);
public:
// 线程池初始化。存活的最少线程数,最大线程数
MyThreadPool(int min, int max);
~MyThreadPool();
// 新增任务
void add(void (*func)(void*), void* arg);
// 获取当前正在工作的线程的数量
int get_busy_num();
// 获取当前存活的线程数量
int get_live_num();
};
#endif
2.2 源文件
#include "threadpool.h"
#include <iostream>
#include <unistd.h>
MyThreadPool::MyThreadPool(int min, int max){
do{
// 初始化一些变量
this->task_queue = nullptr;
this->consumer = nullptr;
this->min_num = min;
this->max_num = max;
this->live_num = min;
this->busy_num = 0;
this->exit_num = 0;
this->destory = false;
// 初始化任务队列
this->task_queue = new TaskQueue();
if(this->task_queue == nullptr){
std::cout << "Failed to initialize task queue..." << std:: endl;
break;
}
// 初始化管理者线程
this->manager = std::thread(MyThreadPool::manage_work, this);
// 初始化消费者线程数组
this->consumer = new MyThread[max];
if(this->consumer == nullptr){
std::cout << "Failed to initialize consumer threads..." << std::endl;
break;
}
for(int i = 0; i < max; i++){
if( i < min ){
this->consumer[i].thread = std::thread(MyThreadPool::consumer_work, this);
this->consumer[i].status = 1;
}else {
this->consumer[i].status = 0;
}
}
std::cout << "successfully initialize threadpool" << std::endl;
return;
}while(0);
if(this->task_queue != nullptr){
delete this->task_queue;
}
if(this->consumer != nullptr){
delete[] this->consumer;
}
}
MyThreadPool::~MyThreadPool(){
// 如果任务还未完成,返回提示
if(this->task_queue->size() != 0){
std::cout << "there are still unfinished tasks!" << std::endl;
}
// 修改线程池销毁标识符,消费者线程将会自动回收,管理者线程需要手动回收
this->destory = true;
this->manager.join();
// 释放任务队列占用内存
if(this->task_queue != nullptr){
delete this->task_queue;
}
// 让依旧被阻塞的子线程运行完毕
for( int i = 0; i < this->live_num; i++ ){
empty.notify_one();
}
// C++要求必须使用join()或detch()显式回收线程
for( int i = 0; i < this->max_num; i++ ){
if(this->consumer[i].thread.joinable()){
this->consumer[i].thread.join();
}
}
if(this->consumer != nullptr){
delete[] consumer;
}
}
// 新增任务
void MyThreadPool::add(void (*func)(void*), void* arg){
Task task;
task.callback = func;
task.arg = arg;
this->task_queue->add(task);
// 唤醒因为任务队列为空而被阻塞的线程
this->empty.notify_one();
}
// 获取当前正在工作的线程数量
int MyThreadPool::get_busy_num(){
this->busy_mutex.lock();
int result = this->busy_num;
this->busy_mutex.unlock();
return result;
}
// 获取当前存活的线程数量
int MyThreadPool::get_live_num(){
this->live_mutex.lock();
int result = this->live_num;
this->live_mutex.unlock();
return result;
}
// 管理者线程
void* MyThreadPool::manage_work(void* arg){
MyThreadPool* pool = static_cast<MyThreadPool*>(arg);
while(!pool->destory){
sleep(3); // 每3秒检测一次线程池
// 查看是否需要新增线程(如果需要,一次增加两个):当任务数量>当前存活线程数 && 当前存活线程数<最大线程数
pool->live_mutex.lock();
if(pool->task_queue->size() > pool->live_num && pool->live_num < pool-> max_num){
int var = 0;
for(int i = 0; i < pool->max_num; i++){
if(pool->consumer[i].status == 0){
pool->consumer[i].status = 1;
pool->consumer[i].thread = std::thread(MyThreadPool::consumer_work, pool);
var++;
pool->live_num++;
}
if(var >= 2 || pool->live_num >= pool->max_num){
break;
}
}
}
pool->live_mutex.unlock();
// 查看是否需要销毁线程(如果需要,一次销毁两个):当任务数量*2<当前存活线程数量 && 当前存活线程数量>最小线程数量
pool->exit_mutex.lock();
pool->live_mutex.lock(); // 重复解锁、加锁操作,是为了和consumer_work内锁的嵌套关系保持一致,避免死锁
if(pool->task_queue->size() * 2 < pool->live_num && pool->live_num > pool->min_num){
for(int i = 0; i < 2; i++){
// 通过修改exit_num变量后唤醒被阻塞的子线程,让子线程以exit_num变量为标志自我消亡
pool->exit_num++;
pool->empty.notify_one();
if(pool->live_num- i <= pool->min_num){
break;
}
}
}
pool->live_mutex.unlock();
pool->exit_mutex.unlock();
// 回收状态为2的消费者线程
for(int i = 0; i < pool->max_num; i++){
if(pool->consumer[i].status == 2){
pool->consumer[i].thread.join();
pool->consumer[i].status = 0;
}
}
}
return 0;
}
// 消费者线程的工作
void* MyThreadPool::consumer_work(void* arg){
MyThreadPool* pool = static_cast<MyThreadPool*>(arg);
// 循环尝试获取任务
while(true){
// 如果线程池没有被摧毁,且当前任务队列为空,线程被阻塞
pool->exit_mutex.lock();
while(pool->task_queue->size() == 0 && !pool->destory){
pool->empty.wait(pool->exit_mutex);
// 判断是否需要杀死子线程
if(pool->exit_num > 0){ // 此时杀死子线程
pool->exit_num--; // 将需要杀死的子线程数量-1
MyThreadPool::thread_exit(pool); // 调用函数,杀死当前子线程
pool->live_mutex.lock(); // 修改目前存活的子线程数量
pool->live_num--;
pool->live_mutex.unlock();
pool->exit_mutex.unlock();
pthread_exit(nullptr);
return 0; // 结束当前线程的任务
}
}
pool->exit_mutex.unlock();
// 如果线程池被摧毁了,那么关闭当前线程
if(pool->destory){
return 0;
}
// 从任务队列中取出一个任务
Task task = pool->task_queue->get();
// 即将开始执行任务,修改当前忙的线程数量
pool->busy_mutex.lock();
pool->busy_num++;
pool->busy_mutex.unlock();
// 执行任务
std::cout << "thread " << std::this_thread::get_id() << " is working..." << std::endl;
task.callback(task.arg);
std::cout << "thread " << std::this_thread::get_id() << " finish working..." << std::endl;
// 执行任务完毕,再次修改当前忙的线程数量
pool->busy_mutex.lock();
pool->busy_num--;
pool->busy_mutex.unlock();
// 最后记得释放变量的内存(这里为了避免生命周期的问题,变量使用的是堆内存)
free(task.arg);
}
return 0;
}
// 修改线程状态,让当前线程死亡
void MyThreadPool::thread_exit(void* arg){
MyThreadPool* pool = static_cast<MyThreadPool*>(arg);
for(int i = 0; i < pool->max_num; i++){
if(pool->consumer[i].status == 1&&
pool->consumer[i].thread.get_id() == std::this_thread::get_id()){
pool->consumer[i].status = 2; // 此处将线程的状态修改为2,提醒管理者线程回收该线程
}
}
}
3. 测试程序
#include "threadpool.h"
#include <iostream>
#include <unistd.h>
void work(void* arg){
int var = *(int*) arg;
std::cout << "线程id==" << std::this_thread::get_id() << ", num == " << var << std::endl;
usleep(100000);
}
int main(void){
MyThreadPool threadpool(3, 8);
for(int i = 0; i < 100; i++){
int *var = new int(i);
threadpool.add(work, var);
}
sleep(10);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!