C++11 线程池实现-ThreadPool

本文详细介绍了C++中的ThreadPool类,包括其构造、enqueue方法和析构函数,展示了如何使用智能指针、任务封装和完美转发来管理线程池,以及异步执行任务的机制。
摘要由CSDN通过智能技术生成

ThreadPool.h文件

#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>

class ThreadPool {
public:
    ThreadPool(size_t);

    //! F&& f: 对可调用对象的右值引用,允许利用移动语义,T&&-右值引用。
    //! Args&&... args: 可变数量的参数,使用完美转发来避免不必要的拷贝,
    //! 同时支持左值和右值。    
    template<class F, class... Args>

    //! std::future对象,其存储的类型为可调用对象调用结果的类型
    //! ->:尾置返回类型,让返回类型依赖于函数参数的类型
    auto enqueue(F&& f, Args&&... args) 
        -> std::future<typename std::result_of<F(Args...)>::type>;
    ~ThreadPool();
private:
    // need to keep track of threads so we can join them
    std::vector< std::thread > workers;
    // the task queue
    std::queue< std::function<void()> > tasks;
    
    // synchronization
    std::mutex queue_mutex;
    std::condition_variable condition;
    bool stop;
};

  /***************************************************************************
  函数名称: ThreadPool
  功能描述: the constructor just launches some amount of workers
  输入参数: threads,表示要创建并运行的工作线线程的数量
  输出参数:
  返 回 值:
  作   者 : zhangsan
  修改日期: 4/9/24 2:59 PM

  修改记录1:// 修改历史记录,包括修改日期、修改者及修改内容
  修改日期:
  版 本 号:
  修 改 人:
  修改内容:
 ****************************************************************************/
inline ThreadPool::ThreadPool(size_t threads)
    :   stop(false)
{
    //! 循环for(size_t i = 0; i < threads; ++i)遍历从0到threads(不包括threads),            
    //! 为每个工作线程创建一次迭代
    for(size_t i = 0;i<threads;++i)

        //! 在workers向量中插入新的工作线程。emplace_back比push_back更高效,因为它                        
        //! 直接在容器内部构造元素,避免了额外的复制或移动操作。
        workers.emplace_back(
            [this] //! 捕获列表
            {
                //! 工作线程的主体是一个无限循环for(;;)
                for(;;)
                {
                    std::function<void()> task;

                    {
                        //! 上锁队列互斥量queue_mutex,保护对共享资源(tasks)的访问
                        std::unique_lock<std::mutex> lock(this->queue_mutex);

                        //! 调用条件变量condition的wait方法使线程进入等待状态,直到有                    
                        //! 任务加入队列或设置stop标志为true
                        this->condition.wait(lock,
                            [this]{ return this->stop || !this->tasks.empty(); });

                        if(this->stop && this->tasks.empty())
                            return;

                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }

                    task();
                }
            }
        );
}
 
 /***************************************************************************
  函数名称: enqueue
  功能描述: add new work item to the pool
  输入参数: F&& f: 对可调用对象的右值引用
            Args&&... args: 可变数量的参数
  输出参数:
  返 回 值:
  作   者 : zhangsan
  修改日期: 4/9/24 2:59 PM

 ****************************************************************************/
template<class F, class... Args>
auto ThreadPool::enqueue(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::unique_lock<std::mutex> lock(queue_mutex);

        // don't allow enqueueing after stopping the pool
        if(stop)
            throw std::runtime_error("enqueue on stopped ThreadPool");

        tasks.emplace([task](){ (*task)(); });
    }

    //! 这行代码调用condition对象的notify_all()方法,该方法用于唤醒所有等待(通过
    //! 调用wait_for()或wait_until()方法在该condition_variable上阻塞)的线程。
    condition.notify_one();
    return res;
}

  /***************************************************************************
  函数名称: ~ThreadPool
  功能描述: the destructor joins all threads
  输入参数: 
  输出参数:
  返 回 值:
  作   者 : zhangsan
  修改日期: 4/9/24 2:59 PM
 ****************************************************************************/
inline ThreadPool::~ThreadPool()
{
    {
        std::unique_lock<std::mutex> lock(queue_mutex);
        stop = true;
    }
    condition.notify_all();
    for(std::thread &worker: workers)
        worker.join();
}

其中下面这段代码涉及到多个高级特性,包括智能指针、任务封装以及完美转发,进行逐步解释

    auto task = std::make_shared< std::packaged_task<return_type()> >(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );

基本概念

  1. std::packaged_task:这是一个模板类,用来封装任何可以调用的目标(如函数、lambda表达式、bind表达式等),以便异步调用。它将调用的结果存储在一个future对象中,从而可以在将来某个时刻获取该结果。

  2. std::make_shared:这是一个函数模板,用于创建一个std::shared_ptr智能指针,其指向新分配的对象。使用std::make_shared通常比直接使用new操作符分配内存并初始化std::shared_ptr更为高效且安全。

  3. std::bind:这是一个函数适配器,用于绑定函数调用的一些或全部参数。

  4. std::forward:这是一个用于实现完美转发的模板函数,它可以保持原始参数的左值/右值属性。

代码解析

  1. 使用std::make_shared< std::packaged_task<return_type()> >(...)创建了一个std::shared_ptr指向std::packaged_task的实例。std::packaged_task内部封装了通过std::bind创建的可调用对象。
  2. 这个std::packaged_task实例与一个std::future<return_type>对象相关联。当任务被执行(即,当你调用std::packaged_taskoperator()时),它的返回结果会自动存储在std::future对象中,这个future对象可以通过std::packaged_taskget_future方法获得。

example.c文件

#include <chrono>
#include <iostream>
#include <vector>

#include "ThreadPool.h"

int main()
{
    
    ThreadPool pool(4);
    std::vector< std::future<int> > results;

    for(int i = 0; i < 8; ++i) {
        results.emplace_back(
            pool.enqueue([i] {
                std::cout << "hello " << i << std::endl;
                std::this_thread::sleep_for(std::chrono::seconds(1));
                std::cout << "world " << i << std::endl;
                return i*i;
            })
        );
    }

    for(auto && result: results)
        std::cout << result.get() << ' ';
    std::cout << std::endl;
    
    return 0;
}

运行结果如下

GitHub 地址:https://github.com/progschj/ThreadPool

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用C++11实现线程池的示例代码: ```cpp #include <iostream> #include <vector> #include <queue> #include <thread> #include <mutex> #include <condition_variable> #include <functional> class ThreadPool { public: ThreadPool(size_t num_threads) { for (size_t i = 0; i < num_threads; ++i) { threads_.emplace_back([this] { while (true) { std::unique_lock<std::mutex> lock(mutex_); condition_.wait(lock, [this] { return !tasks_.empty() || stop_; }); if (stop_ && tasks_.empty()) return; auto task = std::move(tasks_.front()); tasks_.pop(); lock.unlock(); task(); } }); } } ~ThreadPool() { { std::unique_lock<std::mutex> lock(mutex_); stop_ = true; } condition_.notify_all(); for (auto& thread : threads_) thread.join(); } template<class F> void Enqueue(F&& task) { { std::unique_lock<std::mutex> lock(mutex_); tasks_.emplace(std::forward<F>(task)); } condition_.notify_one(); } private: std::vector<std::thread> threads_; std::queue<std::function<void()>> tasks_; std::mutex mutex_; std::condition_variable condition_; bool stop_ = false; }; int main() { ThreadPool pool(4); for (int i = 0; i < 8; ++i) { pool.Enqueue([i] { std::cout << "Task " << i << " is running" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "Task " << i << " is done" << std::endl; }); } return 0; } ``` 此示例实现了一个线程池,其中包含一个任务队列和一组工作线程。构造函数创建指定数量的工作线程,并启动它们以侦听任务队列。Enqueue()方法用于将新任务添加到队列中。每个工作线程将从队列中获取任务并执行它。当线程池被销毁时,所有工作线程都将被停止。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值