基于 C++ 的线程池实现及使用详解

在多线程编程中,线程池是一种非常实用的技术,它可以有效地管理线程的生命周期,避免频繁创建和销毁线程带来的性能开销。本文将详细介绍一个基于 C++ 实现的线程池,包括其实现原理、代码结构以及如何使用该线程池来执行任务。

代码整体结构概述

我们的线程池代码主要由两个类组成:Thread 类和 ThreadPool 类。Thread 类代表一个线程,封装了线程的创建和启动操作;ThreadPool 类则是线程池的核心,负责管理线程的生命周期、任务队列的维护以及任务的分配和执行。

代码详细分析
1. 头文件和常量定义

cpp

#include <unordered_map>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <queue>
#include <iostream>
#include <memory>
#include <atomic>
#include <functional>
#include <thread>
#include <map>
#include <future>

const int TASK_MAX_THRESHHOLD = 1024;
const int THREAD_MAX_THRESHHOLD = 50;
const int THREAD_MAX_IDLE_TIME = 10; // 单位 s

  • 引入了一系列必要的头文件,用于实现线程池所需的各种功能,如多线程同步、内存管理、任务封装等。
  • 定义了三个常量:
    • TASK_MAX_THRESHHOLD:任务队列的最大容量,当任务队列中的任务数量达到该值时,新的任务提交可能会失败。
    • THREAD_MAX_THRESHHOLD:线程池中的最大线程数量,用于限制线程池的并发程度。
    • THREAD_MAX_IDLE_TIME:线程的最大空闲时间,当线程空闲时间超过该值时,可能会被销毁以节省资源。
2. 线程池模式枚举
enum PoolMode//两种模式 
{
    MODE_FIXED, // 静态
    MODE_CACHED // 动态
};

定义了线程池的两种工作模式:

  • MODE_FIXED:静态模式,线程池中的线程数量固定,在创建线程池时指定初始线程数量,后续不会动态调整。
  • MODE_CACHED:动态模式,线程池会根据任务的数量动态调整线程数量,当任务较多时会创建新的线程,当线程空闲时间过长时会销毁线程。
3. Thread 类
class Thread
{
public:
    using ThreadFunc = std::function<void(int)>;
    void start()
    {
        // 创建线程执行
        std::thread t(func_, threadId_);
        t.detach(); // 设置分离线程 
    }

    Thread(ThreadFunc func) :
        func_(func),
        threadId_(generateId_++)
    {}
    ~Thread()
    {
        std::cout << std::this_thread::get_id() << "  " << " Thread" << std::endl;
    }
    int getId()
    {
        return threadId_;
    }
    void setThreadMode(bool isInitThread)
    {
        isInitThread_ = isInitThread;
    }
    bool getThreadMode()
    {
        return isInitThread_;
    }
private:
    ThreadFunc func_;
    std::thread thread_;
    static int generateId_;
    int threadId_; // 保存线程 id
    bool isInitThread_;
};

int Thread::generateId_ = 0;

  • Thread 类代表一个线程,其中:
    • ThreadFunc:定义了线程执行的函数类型,是一个接受 int 类型参数并返回 void 的函数对象。
    • start() 方法:创建一个新的线程,并将 func_ 作为线程的执行函数,同时调用 detach() 方法将线程设置为分离状态,使其在后台独立运行。
    • 构造函数:接受一个 ThreadFunc 类型的参数,用于初始化线程的执行函数,并为线程分配一个唯一的 threadId_
    • getId() 方法:返回线程的唯一标识符。
    • setThreadMode() 和 getThreadMode() 方法:用于设置和获取线程的模式,区分该线程是初始创建的线程还是后续动态创建的线程。
4. ThreadPool 类
构造函数和析构函数
class ThreadPool
{
public:
    ThreadPool()
        : initThreadSize_(0),
        taskSize(0),
        taskQueMaxThreshHold_(TASK_MAX_THRESHHOLD),
        poolMode_(PoolMode::MODE_FIXED),
        isPoolrunning(false),
        idleThreadSize_(0),
        threadSizeThreshHold_(THREAD_MAX_THRESHHOLD),
        curThreadSize_(0),
        stop(false)
    {}
    ~ThreadPool()
    {
        isPoolrunning = false;
        std::unique_lock<std::mutex> lock(taskQueMtx_);
        notEmpty_.notify_all();
        exitCond_.wait(lock, [&]()->bool { return threads_.size() == 0; });
    }

  • 构造函数:初始化线程池的各种参数,包括初始线程数量、任务队列最大容量、线程池模式等。
  • 析构函数:将线程池的运行状态设置为 false,通知所有等待任务的线程,然后等待所有线程退出,确保线程资源被正确释放。
线程池启动和配置方法
    void start(int initThreadSize)
    {
        isPoolrunning = true;
        initThreadSize_ = initThreadSize; // 初始线程数量
        curThreadSize_ = initThreadSize;
        // 创建线程
        for (int i = 0; i < initThreadSize_; i++)
        {
            addThread(true);
        }
    }
    void setTaskQueMaxThreshHold(int threshold)
    {
        if (checkRunningState()) return;
        taskQueMaxThreshHold_ = threshold;
    }
    void setThreadSizeThreshHold(int threshhold)
    {
        if (checkRunningState()) return;
        if (poolMode_ == PoolMode::MODE_CACHED)
        {
            threadSizeThreshHold_ = threshhold;
        }
    }
    void setMode(PoolMode mode)
    {
        if (checkRunningState()) return;
        poolMode_ = mode;
    }

  • start(int initThreadSize) 方法:启动线程池,将线程池的运行状态设置为 true,并根据传入的初始线程数量创建相应数量的线程。
  • setTaskQueMaxThreshHold(int threshold) 方法:设置任务队列的最大容量,只有在线程池未运行时才能修改。
  • setThreadSizeThreshHold(int threshhold) 方法:设置线程池的最大线程数量,仅在动态模式下有效,且只有在线程池未运行时才能修改。
  • setMode(PoolMode mode) 方法:设置线程池的工作模式,只有在线程池未运行时才能修改。
任务提交方法
    template <typename Func, typename... Args>
    auto submitTask(Func&& func, Args&&... args) -> std::future<decltype(func(args...))> {
        // 打包任务放入队列
        using RType = decltype(func(args...));
        auto task = std::make_shared<std::packaged_task<RType()>>(
            std::bind(std::forward<Func>(func), std::forward<Args>(args)...)
        );
        std::future<RType> result = task->get_future();

        // 获取锁
        std::unique_lock<std::mutex> ul(taskQueMtx_);

        // 线程通信  等空余
        if (!notFull_.wait_for(ul, std::chrono::seconds(1), [this]() -> bool {
            return this->taskQue_.size() < (size_t)this->taskQueMaxThreshHold_;
            })) {
            // 等待一秒未满足
            std::cerr << "task queue is full,submit task failed." << std::endl;
            auto newTask = std::make_shared<std::packaged_task<RType()>>(
                []() -> RType { return RType(); }
            );
            return newTask->get_future();
        }

        // 空余就放入队列中
        taskQue_.emplace([task]() {
            (*task)();
        });
        taskSize++;

        // cached模式检查是否要开新的线程,小而快的任务
        std::cout << "Task queue size: " << taskQue_.size() << ", Idle thread size: " << idleThreadSize_ << std::endl;

        // 在notEmpty_上通知
        notEmpty_.notify_all();
        if (poolMode_ == PoolMode::MODE_CACHED
            && taskQue_.size() > idleThreadSize_
            && curThreadSize_ < threadSizeThreshHold_
            ) {
            addThread(false);
            // 修改线程个数相关变量
            curThreadSize_++;
            idleThreadSize_++;
        }
        return result;
    }

  • submitTask 是一个模板函数,用于向线程池提交任务。它接受一个可调用对象 func 及其参数 args,并返回一个 std::future 对象,用于获取任务的执行结果。
    • 首先,使用 std::make_shared 创建一个 std::packaged_task 对象,将 func 和 args 绑定在一起,并通过 get_future() 方法获取与该任务关联的 std::future 对象。
    • 然后,获取任务队列的锁,检查任务队列是否已满。如果队列已满,等待 1 秒钟,如果仍然满则输出错误信息,并创建一个返回默认值的任务,返回其关联的 std::future 对象。
    • 如果队列有空余,将任务封装成一个 lambda 表达式并放入任务队列中,同时增加任务数量。
    • 最后,通知所有等待任务的线程,并根据线程池的模式和任务队列的情况,决定是否需要创建新的线程。
线程执行函数
    void threadFunc(int threadid)
    {
        auto lastTime = std::chrono::high_resolution_clock().now();

        for (;;)
        {
            Task task;
            // 获取锁
            std::cout << "tid:" << std::this_thread::get_id()
                << "尝试获取任务..." << std::endl;
            {
                std::unique_lock<std::mutex> ul(taskQueMtx_);
                // 等通信
                while (taskQue_.size() == 0)
                {
                    if (!isPoolrunning)
                    {
                        std::cout << "threadid:" << std::this_thread::get_id() << " exit thread" << std::endl;
                        threads_.erase(threadid);
                        curThreadSize_--;
                        idleThreadSize_--;
                        exitCond_.notify_all();
                        return;
                    }
                    if (poolMode_ == PoolMode::MODE_CACHED) {
                        if (std::cv_status::timeout == notEmpty_.wait_for(ul, std::chrono::seconds(1)))
                        {
                            auto now = std::chrono::high_resolution_clock().now();
                            auto dur = std::chrono::duration_cast<std::chrono::seconds>(now - lastTime);
                            if (dur.count() >= THREAD_MAX_IDLE_TIME && curThreadSize_ > initThreadSize_)
                            {
                                if (threads_[threadid]->getThreadMode() == false) { // 判断这个线程是不是后续新创建的
                                    std::cout << "threadid:" << std::this_thread::get_id() << " exit thread" << std::endl;
                                    threads_.erase(threadid);
                                    curThreadSize_--;
                                    idleThreadSize_--;
                                }
                                return;
                            }
                        }
                    }
                    else
                    {
                        notEmpty_.wait(ul);
                    }
                }
                // 取任务
                std::cout << "tid:" << std::this_thread::get_id()
                    << "获取任务成功..." << std::endl;
                idleThreadSize_--;
                task = taskQue_.front();
                taskQue_.pop();
                taskSize--;
            }
            if (!taskQue_.empty())
            {
                notEmpty_.notify_all();
            }
            notFull_.notify_all();
            // 跑任务
            if (!task) {
                continue;
            }
            task();
            lastTime = std::chrono::high_resolution_clock().now();
            idleThreadSize_++;
        }
    }

  • threadFunc 是线程的执行函数,每个线程都会执行该函数。
    • 线程进入一个无限循环,不断尝试从任务队列中获取任务。
    • 在获取任务之前,需要获取任务队列的锁,并检查队列是否为空。如果队列为空,根据线程池的模式和线程的空闲时间,决定是否等待任务或退出线程。
    • 如果队列中有任务,取出任务并执行,执行完毕后更新线程的空闲时间和相关状态。
新增线程方法

cpp

    void addThread(bool isInitial)
    {
        auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
        int threadId = ptr->getId();
        if (isInitial) {
            std::cout << "creat init Thread :" << threadId << std::endl;
            ptr->setThreadMode(true);
        }
        else
        {
            std::cout << "creat new success:" << threadId << std::endl;
            ptr->setThreadMode(false);
        }
        threads_.emplace(threadId, std::move(ptr));
        threads_[threadId]->start();
        idleThreadSize_++;
        curThreadSize_++;
    }

  • addThread 方法用于创建新的线程。
    • 使用 std::make_unique 创建一个 Thread 对象,并将 threadFunc 绑定为线程的执行函数。
    • 根据线程是否为初始创建的线程,设置线程的模式。
    • 将线程对象添加到线程池中,并启动线程。
5. 主函数

cpp

int add(int a, int b)
{
    std::cout << "A+B" << std::endl;
    return a + b;
}

int main()
{
    ThreadPool pool;
    pool.setMode(MODE_CACHED);
    pool.start(1);

    std::future<int> fu = pool.submitTask(add, 1, 3);
    std::future<int> fu2 = pool.submitTask(add, 1, 3);
    std::future<int> fu3 = pool.submitTask(add, 1, 3);
    std::future<int> fu4 = pool.submitTask(add, 1, 3);
    std::cout << fu.get() << std::endl;
    return 0;
}

  • 定义了一个简单的函数 add,用于演示任务的执行。
  • 在 main 函数中,创建一个线程池对象,设置为动态模式,并启动线程池,初始线程数量为 1。
  • 向线程池提交 4 个任务,每个任务都是调用 add 函数,并将结果存储在 std::future 对象中。
  • 最后,通过 fu.get() 获取第一个任务的执行结果并输出。
#ifndef THREADPOOL_H
#define THREADPOOL_H
#include <unordered_map>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <queue>
#include <iostream>
#include <memory>
#include <atomic>
#include <functional>
#include <thread>
#include <map>
#include <future>
#include <chrono>
const int TASK_MAX_THRESHHOLD = 1024;
const int THREAD_MAX_THRESHHOLD = 50;
const int THREAD_MAX_IDLE_TIME = 10;//单位 s
enum PoolMode//两种模式 
{
    MODE_FIXED,//静态
    MODE_CACHED,//动态
};
//线程池类型
class Thread
{
public:
    using ThreadFunc = std::function<void(int)>;
    void start()
    {
        //创建线程执行
        std::thread t(func_, threadId_);
        t.detach();//设置分离线程 
 
 
    }
 
    Thread(ThreadFunc func) :
        func_(func)
        , threadId_(generateId_++)
    {}
    ~Thread()
    {
 
        std::cout << std::this_thread::get_id() << "  " << " Thread" << std::endl;
    }
    int getId()
    {
        return threadId_;
    }
    void setThreadMode(bool isInitThread)
    {
        isInitThread_ = isInitThread;
    }
    bool getThreadMode()
    {
        return isInitThread_;
    }
private:
    ThreadFunc func_;
    std::thread thread_;
    static int generateId_;
    int threadId_;//保存线程id
    bool isInitThread_;
};
 
int Thread::generateId_ = 0;
 
 
//线程池类
class ThreadPool
{
public:
    ThreadPool()
        :initThreadSize_(0)
        , taskSize(0)
        , taskQueMaxThreshHold_(TASK_MAX_THRESHHOLD)
        , poolMode_(PoolMode::MODE_FIXED)
        , isPoolrunning(false)
        , idleThreadSize_(0)
        , threadSizeThreshHold_(THREAD_MAX_THRESHHOLD)
        , curThreadSize_(0)
        , stop(false)
    {}
    ~ThreadPool()
    {
 
        isPoolrunning = false;
        std::unique_lock<std::mutex>lock(taskQueMtx_);
 
        notEmpty_.notify_all();
        exitCond_.wait(lock, [&]()->bool {return threads_.size() == 0; });
 
    }
    void start(int initThreadSize)
    {
        isPoolrunning = true;
        initThreadSize_ = initThreadSize;//初始线程数量
        curThreadSize_ = initThreadSize;
        //创建线程
        // creatThreadId_=std::this_thread::get_id();
        for (int i = 0; i < initThreadSize_; i++)
        {
 
            addThread(true);
 
        }
        // 启动
 
    }
    void setTaskQueMaxThreshHold(int threshold)
    {
        if (checkRunningState())return;
        taskQueMaxThreshHold_ = threshold;
    }
    void setThreadSizeThreshHold(int threshhold)
    {
        if (checkRunningState())return;
        if (poolMode_ == PoolMode::MODE_CACHED)
        {
            threadSizeThreshHold_ = threshhold;
        }
    }
 
    void setMode(PoolMode mode)
    {
        if (checkRunningState())return;
        poolMode_ = mode;
    }
    //Result submitTask(std::shared_ptr<Task>sp);
 
 
    template <typename Func, typename... Args>
    auto submitTask(Func&& func, Args&&... args) -> std::future<decltype(func(args...))> {
        // 打包任务放入队列
        using RType = decltype(func(args...));
        auto task = std::make_shared<std::packaged_task<RType()>>(
            std::bind(std::forward<Func>(func), std::forward<Args>(args)...)
        );
        std::future<RType> result = task->get_future();
 
        // 获取锁
        std::unique_lock<std::mutex> ul(taskQueMtx_);
 
        // 线程通信  等空余
        if (!notFull_.wait_for(ul, std::chrono::seconds(1), [this]() -> bool {
            return this->taskQue_.size() < (size_t)this->taskQueMaxThreshHold_;
            })) {
            // 等待一秒未满足
            std::cerr << "task queue is full,submit task failed." << std::endl;
            auto newTask = std::make_shared<std::packaged_task<RType()>>(
                []() -> RType { return RType(); }
            );
            return newTask->get_future();
        }
 
        // 空余就放入队列中
        taskQue_.emplace([task]() {//只能捕获值因为引用计数要加一,如果不加函数没了就销毁了!!
            (*task)();
            });
        taskSize++;
 
        // cached模式检查是否要开新的线程,小而快的任务
        std::cout << "Task queue size: " << taskQue_.size() << ", Idle thread size: " << idleThreadSize_ << std::endl;
 
        // 在notEmpty_上通知
        notEmpty_.notify_all();
        if (poolMode_ == PoolMode::MODE_CACHED
            && taskQue_.size() > idleThreadSize_
            && curThreadSize_ < threadSizeThreshHold_
            ) {
            addThread(false);
            // 修改线程个数相关变量
            curThreadSize_++;
            idleThreadSize_++;
        }
        return result;
    }
 
    ThreadPool(const ThreadPool&) = delete;
    ThreadPool& operator=(const ThreadPool&) = delete;
    void threadFunc(int threadid)
    {
        /*  std::cout << "begin threadFunc" << std::endl;
          std::cout << std::this_thread::get_id() << std::  endl;*/
          //获取锁
        auto lastTime = std::chrono::high_resolution_clock().now();
 
        for (;;)
        {
            Task task;
            //获取锁
            std::cout << "tid:" << std::this_thread::get_id()
                << "尝试获取任务..." << std::endl;
            {//没必要一直拿着锁,取出来任务后就可以释放锁了
                std::unique_lock<std::mutex> ul(taskQueMtx_);
                //等通信
                        //因为超时返回的线程
                while (taskQue_.size() == 0)
                {
                    if (!isPoolrunning)
                    {
                        std::cout << "threadid:" << std::this_thread::get_id() << " exit thread" << std::endl;
                        threads_.erase(threadid);
                        curThreadSize_--;
                        idleThreadSize_--;
                        exitCond_.notify_all();
                        return;
                    }
                    if (poolMode_ == PoolMode::MODE_CACHED) {
                        if (std::cv_status::timeout == notEmpty_.wait_for(ul, std::chrono::seconds(1)))
                        {
                            auto now = std::chrono::high_resolution_clock().now();
                            auto dur = std::chrono::duration_cast<std::chrono::seconds>(now - lastTime);
                            if (dur.count() >= THREAD_MAX_IDLE_TIME && curThreadSize_ > initThreadSize_)
                            {
 
 
                                if (threads_[threadid]->getThreadMode() == true) {//判断这个线程是不是后续新创建的
                                    std::cout << "threadid:" << std::this_thread::get_id() << " exit thread" << std::endl;
                                    threads_.erase(threadid);
                                    curThreadSize_--;
                                    idleThreadSize_--;
                                }
                                return;
                            }
                        }
                    }
                    else
                    {
                        notEmpty_.wait(ul);
                    }
 
                }
                //取任务
                std::cout << "tid:" << std::this_thread::get_id()
                    << "获取任务成功..." << std::endl;
                idleThreadSize_--;
                task = taskQue_.front();
                taskQue_.pop();
                taskSize--;
 
            }
            if (!taskQue_.empty())
            {
                notEmpty_.notify_all();
            }
            notFull_.notify_all();
            //跑任务
            if (task != nullptr)
            {
                //task->run();
                task();
            }
            lastTime = std::chrono::high_resolution_clock().now();
            idleThreadSize_++;
        }
 
    }
    void addThread(bool isInitial)
    {
        auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
        int threadId = ptr->getId();
        if (isInitial) {
            std::cout << "creat init Thread :" << threadId << std::endl;
            ptr->setThreadMode(true);
        }
        else
        {
            std::cout << "creat new success:" << threadId << std::endl;
            ptr->setThreadMode(false);
        }
        threads_.emplace(threadId, std::move(ptr));
        threads_[threadId]->start();
        idleThreadSize_++;
        curThreadSize_++;
 
    }
    bool checkRunningState()
    {
        return isPoolrunning;
    }
    void changeStop()
    {
        stop = !stop;
    }
 
    /*
        example:
        ThreadPool pool;
        pool.start(4);
        pool.submitTask(std::make_shared(MyTask());
    */
private:
    // std::vector<std::unique_ptr<Thread>>threads_;//线程列表
    std::unordered_map<int, std::unique_ptr<Thread>>threads_;
    std::atomic_int curThreadSize_;//当前线程数量
    int threadSizeThreshHold_;//线程上限
    size_t initThreadSize_;//初始线程数量
    std::atomic_int idleThreadSize_;//记录空闲线程数量
    //std::thread::id creatThreadId_;//start里创建的线程id
 
    using Task = std::function<void()>;
    std::queue<Task>taskQue_;//任务队列
    std::atomic_int taskSize;//任务数量
    int taskQueMaxThreshHold_;//任务上限
    std::mutex taskQueMtx_;
 
    std::condition_variable notFull_;//队列不满
    std::condition_variable notEmpty_;//队列不空
    std::condition_variable exitCond_;//等待线程资源回收
 
    PoolMode poolMode_;//当前线程池工作模式
    std::atomic_bool isPoolrunning;//表示是否已经开始
    bool stop;
};
#endif
 
int add(int a, int b)
{
    std::cout << "A+B" << std::endl;
    return a + b;
}
int main()
{
    ThreadPool pool;
    pool.setMode(MODE_CACHED);
    pool.start(1);
     
    std::future<int>fu = pool.submitTask(add, 1, 3);
    std::future<int>fu2 = pool.submitTask(add, 1, 3);
    std::future<int>fu3 = pool.submitTask(add, 1, 3);
    std::future<int>fu4 = pool.submitTask(add, 1, 3);
    std::cout << fu.get() << std::endl;
    return 0;
}

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值