代码功能概述
这段 C++ 代码实现了一个简单的线程池类 MthreadPool
,线程池是一种用于管理和复用线程的机制,它可以避免频繁创建和销毁线程带来的开销,提高程序的性能。MthreadPool
类允许用户指定线程池的最小和最大线程数,并提供了添加任务、管理线程数量等功能。
代码详细解释
1. 类的定义和成员变量
#include "MthreadPool.h"
MthreadPool::MthreadPool(int min, int max) :minthread(min), maxthread(max),
stopthread(false), idlethread(min), currentthread(min)
{
manager = new std::thread(&MthreadPool::Manager, this);
counttask = 0;
addcount = 0;
for (int i = 0; i < max; i++)
{
std::thread t(&MthreadPool::Worker, this);
workers.insert(make_pair(t.get_id(),std::move(t)));
}
}
- 构造函数:
MthreadPool(int min, int max)
:接受两个参数 min
和 max
,分别表示线程池的最小和最大线程数。
- 初始化成员变量:
minthread
和 maxthread
分别存储最小和最大线程数,stopthread
用于标记线程池是否停止,idlethread
表示空闲线程数,初始化为最小线程数,currentthread
表示当前线程数,也初始化为最小线程数。
- 创建一个管理线程:
manager = new std::thread(&MthreadPool::Manager, this);
,该线程负责动态调整线程池中的线程数量。
- 初始化任务计数器:
counttask
记录已执行的任务数,addcount
记录添加的任务数。
- 创建最大数量的工作线程:通过
for
循环创建 max
个工作线程,并将它们存储在 workers
容器中,workers
是一个 std::map
,键为线程的 ID,值为 std::thread
对象。
2. 析构函数
MthreadPool::~MthreadPool()
{
stopthread = true;
condition.notify_all();
for (auto& it : workers)
{
if (it.second.joinable())
{
it.second.join();
}
}
if (manager->joinable())
{
manager->join();
}
delete manager;
}
- 析构函数:
- 将
stopthread
标记为 true
,表示线程池停止工作。
- 使用
condition.notify_all()
唤醒所有等待的线程。
- 遍历
workers
容器,对每个可连接的线程调用 join()
方法,确保所有工作线程都结束。
- 对管理线程
manager
调用 join()
方法,确保管理线程也结束。
- 释放管理线程的内存。
3. 添加任务
void MthreadPool::AddTask(std::function task)
{
{
std::lock_guard locker(queueMutex);
v_vtask.emplace(task);
addcount++;
}
condition.notify_one();
}
- AddTask 方法:
- 接受一个
std::function
类型的任务作为参数。
- 使用
std::lock_guard
加锁,确保在多线程环境下安全地将任务添加到任务队列 v_vtask
中。
- 增加
addcount
计数器,表示添加了一个新任务。
- 使用
condition.notify_one()
唤醒一个等待的工作线程来处理新任务。
4. 管理线程
void MthreadPool::Manager(void)
{
while (!stopthread.load())
{
std::this_thread::sleep_for(std::chrono::seconds(3));
int idle = idlethread.load();
int cur = currentthread.load();
if (idle > cur / 2 && cur > minthread)
{
exithread.store(2);
condition.notify_all();
std::lock_guard lock(exitidMutex);
for (auto id : exit_id)
{
auto thread = workers.find(id);
if (thread != workers.end())
{
(*thread).second.join();
workers.erase(thread);
}
}
exit_id.clear();
}
else if (idle == 0 && cur
- Manager 方法:
- 管理线程的主要职责是定期检查线程池的状态,并根据情况动态调整线程数量。
- 使用
while (!stopthread.load())
循环,只要线程池没有停止,就持续执行管理任务。
- 每 3 秒检查一次线程池状态:
std::this_thread::sleep_for(std::chrono::seconds(3));
。
- 获取当前空闲线程数
idle
和当前线程数 cur
。
- 减少线程:如果空闲线程数超过当前线程数的一半,并且当前线程数大于最小线程数,则让两个线程退出。设置
exithread
为 2,唤醒所有等待的线程,等待线程会检查 exithread
的值并决定是否退出。遍历 exit_id
容器,对要退出的线程调用 join()
方法,并从 workers
容器中移除这些线程。
- 增加线程:如果没有空闲线程,并且当前线程数小于最大线程数,则创建一个新的工作线程,并将其添加到
workers
容器中,同时增加 currentthread
和 idlethread
的值。
5. 工作线程
void MthreadPool::Worker(void)
{
while (!stopthread.load())
{
std::function task = nullptr;
{
std::unique_lock locker(queueMutex);
while (v_vtask.empty() && !stopthread.load())
{
condition.wait(locker);
if (exithread.load() > 0) //work thread exit
{
currentthread--;
exithread--;
idlethread--;
std::lock_guard lock(exitidMutex);
exit_id.emplace_back(std::this_thread::get_id());
return;
}
}
if (!v_vtask.empty())
{
task = std::move(v_vtask.front());
v_vtask.pop();
}
}
if (task)
{
idlethread--;
task();
counttask++;
idlethread++;
}
}
}
- Worker 方法:
- 工作线程的主要职责是从任务队列中获取任务并执行。
- 使用
while (!stopthread.load())
循环,只要线程池没有停止,就持续尝试获取任务。
- 使用
std::unique_lock
加锁,确保在多线程环境下安全地访问任务队列 v_vtask
。
- 如果任务队列为空,并且线程池没有停止,则调用
condition.wait(locker)
进入等待状态,等待新任务的到来。
- 如果
exithread
大于 0,表示有线程需要退出,当前线程减少 currentthread
、exithread
和 idlethread
的值,并将自己的 ID 添加到 exit_id
容器中,然后返回。
- 如果任务队列不为空,从队列中取出一个任务,并将其从队列中移除。
- 执行任务:如果获取到了任务,减少
idlethread
的值,表示当前线程正在工作,执行任务,增加 counttask
的值,表示完成了一个任务,最后增加 idlethread
的值,表示当前线程又空闲了。
代码延申应用
1. 任务优先级支持
在现有的线程池基础上,我们可以添加任务优先级的支持,让重要的任务优先执行。为了实现这一点,我们需要修改任务队列的类型,使用优先队列 std::priority_queue
来存储任务,并为每个任务分配一个优先级。
#include
#include
#include
#include
#include
#include
#include
#include
解释:
- 定义了一个
Task
结构体,包含任务的优先级 priority
和任务函数 task
,并重载了 <
运算符,用于优先队列的比较。
- 修改
AddTask
方法,接受一个额外的 priority
参数,并将任务和优先级封装成 Task
对象添加到优先队列 v_vtask
中。
- 在
Worker
方法中,从优先队列中取出优先级最高的任务执行。
2. 异步任务执行和结果返回
#include
#include
#include
#include
#include
#include
#include
#include
解释:
AddTask
方法使用了模板和 std::packaged_task
来包装传入的任务函数,并将其封装成一个可调用对象。std::packaged_task
可以将一个函数与一个 std::future
关联起来,通过 std::future
可以获取任务的执行结果。
- 在
AddTask
方法中,首先使用 std::result_of
来推断任务函数的返回类型,然后创建一个 std::packaged_task
对象,并将其包装成一个 std::shared_ptr
。接着,通过 std::bind
将任务函数和参数绑定在一起,并将其存储在 std::packaged_task
中。
- 通过
task->get_future()
获取一个 std::future
对象,用于获取任务的执行结果。将封装好的任务添加到任务队列中,并唤醒一个等待的工作线程。
- 在
main
函数中,调用 AddTask
方法添加两个任务,并通过 future.get()
方法获取任务的执行结果。
3. 任务超时处理
在某些情况下,我们可能需要对任务的执行时间进行限制,避免任务长时间运行导致线程池阻塞。可以通过 std::future
的 wait_for
方法来实现任务超时处理。
#include
#include
#include
#include
#include
#include
#include
#include
解释:
- 在
main
函数中,调用 AddTask
方法添加一个长时间运行的任务,并通过 std::future
的 wait_for
方法设置一个超时时间。
wait_for
方法会阻塞当前线程,直到任务完成或超时。如果超时,wait_for
方法会返回 std::future_status::timeout
,否则返回 std::future_status::ready
。
- 根据
wait_for
方法的返回值,判断任务是否超时,并输出相应的信息