手写线程池——C和C++版本

内容参考:爱编程的大丙 (subingwen.cn)

一. 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()为真,说明需要管理者线程对该线程进行回收。

有了以上状态码,我们可以让管理者线程每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;
}
posted @   BinaryPrinter  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示