线程池代码分析及延申应用

代码功能概述

这段 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,表示有线程需要退出,当前线程减少 currentthreadexithread 和 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 方法的返回值,判断任务是否超时,并输出相应的信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值