代码功能概述
这段 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<void(void)> task)
{
{
std::lock_guard<std::mutex> locker(queueMutex);
v_vtask.emplace(task);
addcount++;
}
condition.notify_one();
}
- AddTask 方法:
- 接受一个
std::function<void(void)>
类型的任务作为参数。 - 使用
std::lock_guard<std::mutex>
加锁,确保在多线程环境下安全地将任务添加到任务队列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<std::mutex> 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<maxthread)
{
std::thread t(&MthreadPool::Worker, this);
workers.insert(make_pair(t.get_id(), std::move(t)));
currentthread++;
idlethread++;
}
}
}
- 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<void(void)> task = nullptr;
{
std::unique_lock<std::mutex> locker(queueMutex);
while (v_vtask.empty() && !stopthread.load())
{
condition.wait(locker);
if (exithread.load() > 0) //work thread exit
{
currentthread--;
exithread--;
idlethread--;
std::lock_guard<std::mutex> 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<std::mutex>
加锁,确保在多线程环境下安全地访问任务队列v_vtask
。 - 如果任务队列为空,并且线程池没有停止,则调用
condition.wait(locker)
进入等待状态,等待新任务的到来。 - 如果
exithread
大于 0,表示有线程需要退出,当前线程减少currentthread
、exithread
和idlethread
的值,并将自己的 ID 添加到exit_id
容器中,然后返回。 - 如果任务队列不为空,从队列中取出一个任务,并将其从队列中移除。
- 执行任务:如果获取到了任务,减少
idlethread
的值,表示当前线程正在工作,执行任务,增加counttask
的值,表示完成了一个任务,最后增加idlethread
的值,表示当前线程又空闲了。
代码延申应用
1. 任务优先级支持
在现有的线程池基础上,我们可以添加任务优先级的支持,让重要的任务优先执行。为了实现这一点,我们需要修改任务队列的类型,使用优先队列 std::priority_queue
来存储任务,并为每个任务分配一个优先级。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <functional>
#include <atomic>
#include <map>
#include <vector>
class MthreadPool {
public:
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(std::make_pair(t.get_id(), std::move(t)));
}
}
~MthreadPool()
{
stopthread = true;
condition.notify_all();
for (auto& it : workers)
{
if (it.second.joinable())
{
it.second.join();
}
}
if (manager->joinable())
{
manager->join();
}
delete manager;
}
void AddTask(std::function<void(void)> task, int priority)
{
{
std::lock_guard<std::mutex> locker(queueMutex);
v_vtask.emplace(priority, task);
addcount++;
}
condition.notify_one();
}
private:
struct Task {
int priority;
std::function<void(void)> task;
bool operator<(const Task& other) const {
return priority < other.priority;
}
};
int minthread;
int maxthread;
std::atomic<bool> stopthread;
std::atomic<int> idlethread;
std::atomic<int> currentthread;
std::atomic<int> exithread{ 0 };
std::atomic<int> counttask;
std::atomic<int> addcount;
std::map<std::thread::id, std::thread> workers;
std::thread* manager;
std::priority_queue<Task> v_vtask;
std::mutex queueMutex;
std::mutex exitidMutex;
std::condition_variable condition;
std::vector<std::thread::id> exit_id;
void 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<std::mutex> 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 < maxthread)
{
std::thread t(&MthreadPool::Worker, this);
workers.insert(std::make_pair(t.get_id(), std::move(t)));
currentthread++;
idlethread++;
}
}
}
void Worker(void)
{
while (!stopthread.load())
{
std::function<void(void)> task = nullptr;
{
std::unique_lock<std::mutex> locker(queueMutex);
while (v_vtask.empty() && !stopthread.load())
{
condition.wait(locker);
if (exithread.load() > 0) //work thread exit
{
currentthread--;
exithread--;
idlethread--;
std::lock_guard<std::mutex> lock(exitidMutex);
exit_id.emplace_back(std::this_thread::get_id());
return;
}
}
if (!v_vtask.empty())
{
task = std::move(v_vtask.top().task);
v_vtask.pop();
}
}
if (task)
{
idlethread--;
task();
counttask++;
idlethread++;
}
}
}
};
// 使用示例
void taskFunction() {
std::cout << "Task is running." << std::endl;
}
int main() {
MthreadPool pool(2, 5);
pool.AddTask(taskFunction, 2);
pool.AddTask(taskFunction, 1);
pool.AddTask(taskFunction, 3);
std::this_thread::sleep_for(std::chrono::seconds(10));
return 0;
}
解释:
- 定义了一个
Task
结构体,包含任务的优先级priority
和任务函数task
,并重载了<
运算符,用于优先队列的比较。 - 修改
AddTask
方法,接受一个额外的priority
参数,并将任务和优先级封装成Task
对象添加到优先队列v_vtask
中。 - 在
Worker
方法中,从优先队列中取出优先级最高的任务执行。
2. 异步任务执行和结果返回
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <functional>
#include <atomic>
#include <map>
#include <vector>
#include <future>
class MthreadPool {
public:
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(std::make_pair(t.get_id(), std::move(t)));
}
}
~MthreadPool()
{
stopthread = true;
condition.notify_all();
for (auto& it : workers)
{
if (it.second.joinable())
{
it.second.join();
}
}
if (manager->joinable())
{
manager->join();
}
delete manager;
}
template<typename F, typename... Args>
auto AddTask(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type> {
using return_type = typename std::result_of<F(Args...)>::type;
auto task = std::make_shared< std::packaged_task<return_type()> >(
std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);
std::future<return_type> res = task->get_future();
{
std::lock_guard<std::mutex> lock(queueMutex);
if (stopthread)
throw std::runtime_error("enqueue on stopped ThreadPool");
v_vtask.emplace([task]() { (*task)(); });
addcount++;
}
condition.notify_one();
return res;
}
private:
int minthread;
int maxthread;
std::atomic<bool> stopthread;
std::atomic<int> idlethread;
std::atomic<int> currentthread;
std::atomic<int> exithread{ 0 };
std::atomic<int> counttask;
std::atomic<int> addcount;
std::map<std::thread::id, std::thread> workers;
std::thread* manager;
std::queue<std::function<void()>> v_vtask;
std::mutex queueMutex;
std::mutex exitidMutex;
std::condition_variable condition;
std::vector<std::thread::id> exit_id;
void 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<std::mutex> 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 < maxthread)
{
std::thread t(&MthreadPool::Worker, this);
workers.insert(std::make_pair(t.get_id(), std::move(t)));
currentthread++;
idlethread++;
}
}
}
void Worker(void)
{
while (!stopthread.load())
{
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(queueMutex);
condition.wait(lock, [this] { return stopthread || !v_vtask.empty(); });
if (stopthread && v_vtask.empty())
return;
if (exithread.load() > 0)
{
currentthread--;
exithread--;
idlethread--;
std::lock_guard<std::mutex> lock(exitidMutex);
exit_id.emplace_back(std::this_thread::get_id());
return;
}
task = std::move(v_vtask.front());
v_vtask.pop();
}
idlethread--;
task();
counttask++;
idlethread++;
}
}
};
// 使用示例
int add(int a, int b) {
return a + b;
}
int main() {
MthreadPool pool(2, 5);
auto future1 = pool.AddTask(add, 2, 3);
auto future2 = pool.AddTask(add, 4, 5);
std::cout << "Result 1: " << future1.get() << std::endl;
std::cout << "Result 2: " << future2.get() << std::endl;
return 0;
}
解释:
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 <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <functional>
#include <atomic>
#include <map>
#include <vector>
#include <future>
#include <chrono>
class MthreadPool {
public:
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(std::make_pair(t.get_id(), std::move(t)));
}
}
~MthreadPool()
{
stopthread = true;
condition.notify_all();
for (auto& it : workers)
{
if (it.second.joinable())
{
it.second.join();
}
}
if (manager->joinable())
{
manager->join();
}
delete manager;
}
template<typename F, typename... Args>
auto AddTask(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type> {
using return_type = typename std::result_of<F(Args...)>::type;
auto task = std::make_shared< std::packaged_task<return_type()> >(
std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);
std::future<return_type> res = task->get_future();
{
std::lock_guard<std::mutex> lock(queueMutex);
if (stopthread)
throw std::runtime_error("enqueue on stopped ThreadPool");
v_vtask.emplace([task]() { (*task)(); });
addcount++;
}
condition.notify_one();
return res;
}
private:
int minthread;
int maxthread;
std::atomic<bool> stopthread;
std::atomic<int> idlethread;
std::atomic<int> currentthread;
std::atomic<int> exithread{ 0 };
std::atomic<int> counttask;
std::atomic<int> addcount;
std::map<std::thread::id, std::thread> workers;
std::thread* manager;
std::queue<std::function<void()>> v_vtask;
std::mutex queueMutex;
std::mutex exitidMutex;
std::condition_variable condition;
std::vector<std::thread::id> exit_id;
void 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<std::mutex> 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 < maxthread)
{
std::thread t(&MthreadPool::Worker, this);
workers.insert(std::make_pair(t.get_id(), std::move(t)));
currentthread++;
idlethread++;
}
}
}
void Worker(void)
{
while (!stopthread.load())
{
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(queueMutex);
condition.wait(lock, [this] { return stopthread || !v_vtask.empty(); });
if (stopthread && v_vtask.empty())
return;
if (exithread.load() > 0)
{
currentthread--;
exithread--;
idlethread--;
std::lock_guard<std::mutex> lock(exitidMutex);
exit_id.emplace_back(std::this_thread::get_id());
return;
}
task = std::move(v_vtask.front());
v_vtask.pop();
}
idlethread--;
task();
counttask++;
idlethread++;
}
}
};
// 使用示例
int longRunningTask() {
std::this_thread::sleep_for(std::chrono::seconds(5));
return 42;
}
int main() {
MthreadPool pool(2, 5);
auto future = pool.AddTask(longRunningTask);
std::chrono::seconds timeout(2);
if (future.wait_for(timeout) == std::future_status::timeout) {
std::cout << "Task timed out." << std::endl;
} else {
std::cout << "Task result: " << future.get() << std::endl;
}
return 0;
}
解释:
- 在
main
函数中,调用AddTask
方法添加一个长时间运行的任务,并通过std::future
的wait_for
方法设置一个超时时间。 wait_for
方法会阻塞当前线程,直到任务完成或超时。如果超时,wait_for
方法会返回std::future_status::timeout
,否则返回std::future_status::ready
。- 根据
wait_for
方法的返回值,判断任务是否超时,并输出相应的信息