C++ 线程池设计

1. 设计思路与原理

线程池是一种常见的并发编程模式,旨在管理和复用多个线程以避免频繁创建和销毁线程的开销。线程池中的线程执行任务队列中的任务,而任务的调度和管理由管理组件负责。回调函数通常用于任务完成后的通知或下一步操作。以下是相关组件的设计概述:

  • 线程池(Thread Pool):维护一个固定数量的线程,负责从任务队列中取任务执行。线程池应支持任务的动态提交和任务执行状态的反馈。
  • 任务队列(Task Queue):保存待执行的任务,通常是线程安全的队列。任务可以是不同类型的操作,执行完后可以触发回调函数。
  • 工作队列(Work Queue):与任务队列相似,有时可以直接将任务放到工作队列中,由工作线程直接处理。
  • 管理组件(Management Component):管理线程池的生命周期、任务调度和回调处理等。
  • 回调函数(Callback Functions):任务执行完成后,执行回调函数以通知任务状态或执行其他后续操作。

2. 线程池工作流程

  • 线程池创建时会启动若干个工作线程。
  • 任务管理器向任务队列中添加任务。
  • 每个工作线程等待任务队列中的任务,一旦获取到任务则执行该任务。
  • 任务执行结束后,调用回调函数通知任务完成。

3. 流程图解释

3.1. 线程池任务执行流程

+-------------------------+       +--------------------+
| TaskManager (管理组件)   |       | ThreadPool (线程池) |
+-------------------------+       +--------------------+
           |                               |
           |                               |
           |                               V
           |                      +------------------+
           |                      | TaskQueue (任务队列) |
           |                      +------------------+
           |                               |
           |                               V
           +----------------------> Task Added
                                       |
                                       V
                        +-------------------------------+
                        | Worker Thread (工作线程)       |
                        +-------------------------------+
                                       |
                                       V
                            Execute Task (执行任务)
                                       |
                                       V
                           Call Callback Function (调用回调)

  • TaskManager 负责将任务添加到 TaskQueue
  • ThreadPool 中的每个 Worker Thread 持续从 TaskQueue 中获取任务并执行。
  • 执行任务后,会调用 Callback Function,例如通知主线程任务完成。
3.2. 线程池组件的设计图

+---------------------------------------------------+
|                    TaskManager                   |
| +---------------------------------------------+   |
| |             TaskQueue (任务队列)          | |
| | +-------------------+     +---------------+|   |
| | | Add Task           |     | Get Task      || |
| | +-------------------+     +---------------+|   |
| +---------------------------------------------+   |
|                                                   |
| +---------------------------------------------+   |
| |             ThreadPool (线程池)             |   |
| | +-------------------+  +-----------------+ |   |
| | | Start Threads      |  | Stop Threads    | | |
| | +-------------------+  +-----------------+ |   |
| +---------------------------------------------+   |
+---------------------------------------------------+
        |                                          |
        V                                          V
+----------------+                      +----------------+
| Worker Thread 1|                      | Worker Thread 2|
+----------------+                      +----------------+
        |                                            |
        V                                          V
 Execute Task 1 (任务1)               Execute Task 2 (任务2)
        |                                            |
        V                                          V
 Call Callback (回调)               Call Callback (回调)

  • TaskQueue 是一个线程安全的任务存储区,线程池中的多个工作线程会从任务队列中取出任务并执行。
  • 每个 Worker Thread 都是一个独立的工作线程,持续执行队列中的任务。
  • 当一个任务完成时,它会调用指定的回调函数,通常用于通知任务管理器或调用者任务已完成。
3.3. 任务执行和回调流程

+-------------------------+
| Worker Thread           |
| (线程池中的一个工作线程) |
+-------------------------+
          |
          V
    Execute Task (任务执行)
          |
          V
    Call Callback (回调通知)
          |
          V
+-------------------------+
| Callback Function        |
| (回调函数,通知任务完成) |
+-------------------------+

4. 设计思想与解释

  1. 线程池的设计

    • 线程池的核心目的是复用多个线程,避免频繁创建和销毁线程带来的开销。线程池中的线程在程序运行过程中一直存在,并循环等待任务。
    • 设计线程池时,需要解决线程的同步问题,确保多个线程能够安全地从任务队列中获取任务。
  2. 任务队列的设计

    • 任务队列采用了线程安全的设计,使用 std::mutexstd::condition_variable 来确保多个线程能够安全地访问任务队列,避免数据竞争。
    • 任务队列是生产者-消费者模型的一个典型实现,线程池中的线程是消费者,管理组件是生产者。
  3. 任务的添加与执行

    • 管理组件通过向任务队列添加任务,将工作分配给线程池。每个线程从队列中获取任务并执行。
    • 任务的执行是异步的,通过 回调函数 的方式通知主线程任务的完成状态。
  4. 回调函数的设计

    • 回调函数用于任务执行完成后,向外部传递任务结果。它为任务的异步执行提供了一个反馈机制,避免阻塞主线程。

5.设计思路扩展:

  1. 多类型任务:任务不仅限于一种类型,还可以有 I/O 任务、计算任务、定时任务等,每种任务有不同的执行方式和处理机制。
  2. 任务优先级:任务队列支持优先级调度,重要任务可以优先执行。
  3. 任务依赖和回调链:任务之间可以存在依赖,任务完成时触发其他任务或回调。
  4. 更多的线程池管理功能:支持动态调整线程数量、任务的取消等高级功能。
流程图扩展
1. 多种任务类型的处理流程

+-------------------------+
| TaskManager (管理组件)   |
+-------------------------+
           |
           | 添加任务 (I/O, 计算, 定时等)
           V
  +-------------------------------+
  | TaskQueue (任务队列,带优先级) |
  +-------------------------------+
           |
           V
  +-------------------------------+
  |  Worker Thread 1  (工作线程)   |
  +-------------------------------+
           |
           V
     执行 I/O 任务 (I/O Task)
           |
           V
  调用 I/O 回调函数


2. 任务优先级和多任务类型的队列设计

+----------------------------------+
| TaskQueue (任务队列,支持优先级)  |
+----------------------------------+
| Priority: High                   |
| [I/O Task 1]                     |
| [Compute Task 3]                 |
+----------------------------------+
| Priority: Medium                 |
| [Compute Task 1]                 |
+----------------------------------+
| Priority: Low                    |
| [Scheduled Task 1]               |
| [I/O Task 2]                     |
+----------------------------------+

任务队列中根据优先级对任务进行调度,线程池中的工作线程优先从高优先级的队列中取任务执行。

1. 设计思路

本设计的核心是实现一个线程池,它能够调度不同优先级的任务,并执行任务后调用相应的回调函数。整个设计分为多个组件,各司其职,组成一个灵活高效的任务处理系统。以下是详细设计思路:

1.1 主要组件
  • 任务类 (Task):封装了具体的任务函数、优先级和回调函数。
  • 任务队列 (TaskQueue):一个支持优先级调度的任务队列,使用了优先级队列存储任务。高优先级的任务会先被执行。
  • 线程池 (ThreadPool):维护了多个工作线程,线程从任务队列中提取任务执行,并调用回调函数。可以动态添加任务,并能够安全停止所有工作线程。
  • 任务管理器 (TaskManager):为用户提供接口,添加任务到线程池,并负责调度任务的执行和停止。
1.2 线程池工作流程
  1. 任务提交:用户通过 TaskManager 将任务添加到 ThreadPool 中。
  2. 任务调度:线程池中的每个线程从 TaskQueue 取出任务,按优先级执行。
  3. 任务执行:任务执行完成后,调用回调函数,通知任务完成。
  4. 线程停止:当所有任务处理完成,或者用户显式要求停止时,线程池会安全停止所有线程。
2. 知识要点讲解
2.1 线程池的优点
  • 资源复用:线程池中的线程在整个程序生命周期内被复用,避免频繁创建和销毁线程。
  • 任务并发处理:线程池可以同时处理多个任务,充分利用 CPU 多核能力。
  • 任务调度灵活性:通过优先级队列可以调度不同优先级的任务,确保高优先级任务被优先执行。
2.2 任务队列与调度
  • 优先级调度:使用 std::priority_queue 数据结构,任务根据优先级被有序存储,线程从队列中提取任务时,总是优先执行高优先级任务。
  • 线程同步:通过 std::mutexstd::condition_variable 来保护任务队列,确保多线程操作队列时的安全性。同时,条件变量 cv_ 用于通知线程有新任务可以执行。
2.3 任务与回调函数
  • 任务函数:任务通过 std::function<void()> 封装具体的逻辑,任务类型包括 I/O、计算任务等。
  • 回调函数:回调函数用于任务完成后,执行一些附加操作或通知任务状态。
2.4 线程池的停止机制
  • 任务队列的停止:当线程池需要停止时,任务队列会唤醒所有等待的线程,通知它们可以退出。
  • 线程安全退出:工作线程会检查任务队列的停止标志,如果发现队列已经停止且无任务可执行,线程会安全退出。
3. 流程图

下面是线程池执行任务的流程图,展示了任务从添加到执行完成并回调的整个过程。

+---------------------+
|   主程序开始        |
+---------+-----------+
          |
          v
+---------------------+     任务通过TaskManager添加到任务队列
| 添加任务到TaskManager|---------------------------------------->+-----------------------+
+---------------------+                                          | 任务队列 (TaskQueue)   |
                                                                  |    任务按优先级存储    |
                                                                  +-----------+-----------+
                                                                              |
                                                                              v
                                                                  +-----------------------+
                                                                  | 线程池 (ThreadPool)    |
                                                                  |  获取任务并执行       |
                                                                  +-----------+-----------+
                                                                              |
                           +--------------------------------------------------+
                           |
                           v
            +-------------------------+          任务执行后,调用回调函数
            | 执行任务 (Task::execute) |-------------------------------------->+---------------------+
            +-------------------------+                                       | 回调函数执行完成    |
                                                                               +---------------------+
                           |
                           v
            +--------------------------+
            |   检查是否停止 (stop)     |
            +--------------------------+
                           |
                           v
            +-------------------------+       继续执行下一个任务或终止线程池
            | 线程安全退出或继续工作   |
            +-------------------------+
                           |
                           v
               +------------------+
               |  主程序结束      |
               +------------------+

4. 注意事项
4.1 线程池的停止

为了防止程序卡住在 getTaskwait 调用处,确保在程序结束时所有线程可以安全退出。通过 stop() 方法通知所有等待的线程,让它们退出 while 循环。

4.2 优先级队列

为了确保任务调度合理,使用 std::priority_queue 存储任务。通过自定义比较器 CompareTask,高优先级的任务会先出队列。

4.3 回调函数的作用

回调函数用于通知任务已经完成。在某些场景中,如更新 UI 或日志记录,回调函数能确保异步任务结束后执行后续逻辑。

4.4 线程同步

为了避免多个线程竞争访问任务队列,使用了互斥锁 (std::mutex) 来保护队列的并发访问。同时通过条件变量 (std::condition_variable) 进行线程间的通信,确保线程在有新任务加入时被唤醒。

6.示例代码

#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <vector>
#include <future>
#include <chrono>

// 定义任务类型枚举
enum class TaskType { IO, Compute, Scheduled };

// 任务优先级枚举
enum class Priority { High, Medium, Low };

// 定义任务类,包含任务类型、优先级和回调函数
class Task {
public:
    // 构造函数:传入任务类型、优先级、任务函数和回调函数
    Task(TaskType type, Priority priority, std::function<void()> taskFunc, std::function<void()> callback)
        : type_(type), priority_(priority), taskFunc_(taskFunc), callback_(callback) {}

    // 执行任务函数和回调函数
    void execute() {
        taskFunc_();   // 执行任务
        callback_();   // 执行回调
    }

    // 获取任务的优先级,用于任务调度
    Priority getPriority() const {
        return priority_;
    }

private:
    TaskType type_;  // 任务类型
    Priority priority_;  // 任务优先级
    std::function<void()> taskFunc_;  // 任务函数
    std::function<void()> callback_;  // 回调函数
};

// 比较器用于优先级队列,优先处理高优先级任务
struct CompareTask {
    // 重载操作符(),使得优先级高的任务先执行
    bool operator()(const Task& t1, const Task& t2) {
        return static_cast<int>(t1.getPriority()) > static_cast<int>(t2.getPriority());
    }
};

// 任务队列类,支持优先级调度
class TaskQueue {
public:
    // 添加任务到队列中,按优先级存储
    void addTask(Task task) {
        std::lock_guard<std::mutex> lock(mtx_);
        tasks_.push(task);
        cv_.notify_one();  // 通知等待中的线程
    }

    // 获取任务并从队列中删除,支持线程池终止
    bool getTask(Task& task) {
        std::unique_lock<std::mutex> lock(mtx_);
        // 等待任务或线程池停止的条件满足
        cv_.wait(lock, [this]() { return stop_ || !tasks_.empty(); });

        // 如果队列为空且线程池停止,返回 false 表示没有任务可执行
        if (tasks_.empty() && stop_) {
            return false;
        }

        // 取出队列中的任务
        task = tasks_.top();
        tasks_.pop();
        return true;
    }

    // 停止任务队列并唤醒所有线程
    void stop() {
        std::lock_guard<std::mutex> lock(mtx_);
        stop_ = true;
        cv_.notify_all();  // 通知所有等待中的线程,防止它们一直阻塞
    }

private:
    std::priority_queue<Task, std::vector<Task>, CompareTask> tasks_;  // 优先级队列
    std::mutex mtx_;  // 保护任务队列的互斥锁
    std::condition_variable cv_;  // 条件变量用于任务通知
    bool stop_ = false;  // 停止标志
};

// 线程池类
class ThreadPool {
public:
    // 构造函数:初始化线程池,并创建指定数量的工作线程
    ThreadPool(size_t numThreads) : stopFlag_(false) {
        for (size_t i = 0; i < numThreads; ++i) {
            workers_.emplace_back([this]() {
                while (true) {
                    Task task(TaskType::IO, Priority::Low, [] {}, [] {});  // 临时任务对象
                    if (!this->taskQueue_.getTask(task)) {
                        // 如果无法获取任务且线程池被停止,则退出
                        break;
                    }
                    task.execute();  // 执行任务
                }
                });
        }
    }

    // 添加任务到线程池中
    void enqueueTask(Task task) {
        taskQueue_.addTask(task);
    }

    // 停止线程池
    void stop() {
        taskQueue_.stop();  // 停止任务队列
        for (auto& worker : workers_) {
            if (worker.joinable()) {
                worker.join();  // 等待所有工作线程结束
            }
        }
    }

    ~ThreadPool() {
        stop();  // 析构时确保线程池被正确停止
    }

private:
    std::vector<std::thread> workers_;  // 工作线程列表
    TaskQueue taskQueue_;  // 任务队列
    bool stopFlag_;  // 停止标志
};

// 回调函数类型
using Callback = std::function<void()>;

// 定义不同类型的任务函数
void ioTask(int id) {
    std::this_thread::sleep_for(std::chrono::seconds(1));  // 模拟I/O操作
    std::cout << "I/O Task " << id << " completed" << std::endl;
}

void computeTask(int id) {
    std::this_thread::sleep_for(std::chrono::milliseconds(500));  // 模拟计算任务
    std::cout << "Compute Task " << id << " completed" << std::endl;
}

void scheduledTask(int id) {
    std::cout << "Scheduled Task " << id << " completed" << std::endl;
}

// 管理组件,负责任务添加和调度
class TaskManager {
public:
    // 构造函数:初始化线程池
    TaskManager(size_t numThreads) : threadPool_(numThreads) {}

    // 添加 I/O 任务
    void addIOTask(int id, Priority priority) {
        threadPool_.enqueueTask(Task(TaskType::IO, priority, [id]() { ioTask(id); }, [id]() {
            std::cout << "Callback: I/O Task " << id << " completed callback!" << std::endl;
            }));
    }

    // 添加计算任务
    void addComputeTask(int id, Priority priority) {
        threadPool_.enqueueTask(Task(TaskType::Compute, priority, [id]() { computeTask(id); }, [id]() {
            std::cout << "Callback: Compute Task " << id << " completed callback!" << std::endl;
            }));
    }

    // 添加定时任务
    void addScheduledTask(int id, Priority priority) {
        threadPool_.enqueueTask(Task(TaskType::Scheduled, priority, [id]() { scheduledTask(id); }, [id]() {
            std::cout << "Callback: Scheduled Task " << id << " completed callback!" << std::endl;
            }));
    }

    // 停止所有任务
    void stopAllTasks() {
        threadPool_.stop();
        std::cout << "All tasks stopped." << std::endl;
    }

private:
    ThreadPool threadPool_;  // 线程池实例
};

// 主函数
int main() {
    TaskManager manager(4);  // 创建4个线程的线程池

    // 添加各种类型的任务并指定优先级
    manager.addIOTask(1, Priority::High);
    manager.addComputeTask(2, Priority::Medium);
    manager.addScheduledTask(3, Priority::Low);
    manager.addIOTask(4, Priority::Medium);
    manager.addComputeTask(5, Priority::High);
    manager.addScheduledTask(6, Priority::Low);

    // 等待任务完成
    std::this_thread::sleep_for(std::chrono::seconds(5));

    // 停止线程池
    manager.stopAllTasks();

    return 0;
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值