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. 设计思想与解释
-
线程池的设计:
- 线程池的核心目的是复用多个线程,避免频繁创建和销毁线程带来的开销。线程池中的线程在程序运行过程中一直存在,并循环等待任务。
- 设计线程池时,需要解决线程的同步问题,确保多个线程能够安全地从任务队列中获取任务。
-
任务队列的设计:
- 任务队列 采用了线程安全的设计,使用
std::mutex
和std::condition_variable
来确保多个线程能够安全地访问任务队列,避免数据竞争。 - 任务队列是生产者-消费者模型的一个典型实现,线程池中的线程是消费者,管理组件是生产者。
- 任务队列 采用了线程安全的设计,使用
-
任务的添加与执行:
- 管理组件通过向任务队列添加任务,将工作分配给线程池。每个线程从队列中获取任务并执行。
- 任务的执行是异步的,通过 回调函数 的方式通知主线程任务的完成状态。
-
回调函数的设计:
- 回调函数用于任务执行完成后,向外部传递任务结果。它为任务的异步执行提供了一个反馈机制,避免阻塞主线程。
5.设计思路扩展:
- 多类型任务:任务不仅限于一种类型,还可以有 I/O 任务、计算任务、定时任务等,每种任务有不同的执行方式和处理机制。
- 任务优先级:任务队列支持优先级调度,重要任务可以优先执行。
- 任务依赖和回调链:任务之间可以存在依赖,任务完成时触发其他任务或回调。
- 更多的线程池管理功能:支持动态调整线程数量、任务的取消等高级功能。
流程图扩展
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 线程池工作流程
- 任务提交 :用户通过
TaskManager
将任务添加到ThreadPool
中。 - 任务调度 :线程池中的每个线程从
TaskQueue
取出任务,按优先级执行。 - 任务执行:任务执行完成后,调用回调函数,通知任务完成。
- 线程停止:当所有任务处理完成,或者用户显式要求停止时,线程池会安全停止所有线程。
2. 知识要点讲解
2.1 线程池的优点
- 资源复用:线程池中的线程在整个程序生命周期内被复用,避免频繁创建和销毁线程。
- 任务并发处理:线程池可以同时处理多个任务,充分利用 CPU 多核能力。
- 任务调度灵活性:通过优先级队列可以调度不同优先级的任务,确保高优先级任务被优先执行。
2.2 任务队列与调度
- 优先级调度 :使用
std::priority_queue
数据结构,任务根据优先级被有序存储,线程从队列中提取任务时,总是优先执行高优先级任务。 - 线程同步 :通过
std::mutex
和std::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 线程池的停止
为了防止程序卡住在 getTask
的 wait
调用处,确保在程序结束时所有线程可以安全退出。通过 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;
}