C++ 线程池

文章介绍了C++11中的future概念及其在异步通信中的作用,future可以获取async线程函数的返回结果。文章通过分析ThreadPool类的源码,展示了如何使用future和线程池实现多线程任务调度,包括任务队列、线程同步和等待机制。在示例中,创建了一个线程池并使用future存储异步任务的结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GitHub 地址:GitHub - progschj/ThreadPool: A simple C++11 Thread Pool implementation
本次是注释这个源程序,加上自己的理解

std::vector< std::future<int> > results;   
  C++ <future>头文件里线程类
  作用:c++11引入的future是为了解决异步通信问题的。future可以看做是数据通道,
  可以获取到async的线程函数的返回结果,也可以与promise连用,获取promise的数据。 
    
    在C++11之前我们在线程A中创建子线程B,为了拿到B中计算的数据,我们需要声明一个全局变量保存B的计算结果数据,线程A等待,直到B计算完成后,读取全局变量,拿到计算结果数据。C++11之后,我们可以声明future通过get方法获取异步返回值,很大程度的简化了操作步骤

std::future接口

template<typename ResultType>
class future
{
public:
  future() noexcept;
  future(future&&) noexcept;
  future& operator=(future&&) noexcept;
  ~future();
  
  future(future const&) = delete;
  future& operator=(future const&) = delete;


  bool valid() const noexcept;
  
  ResultType get();
  shared_future<ResultType> share();

  void wait();

  template<typename Rep,typename Period>
  future_status wait_for(
      std::chrono::duration<Rep,Period> const& relative_time);

  template<typename Clock,typename Duration>
  future_status wait_until(
      std::chrono::time_point<Clock,Duration> const& absolute_time);
};
#ifndef THREAD_POOL_H
#define THREAD_POOL_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);
    template<class F, class... Args>//C++11的新特性--可变模版参数,它对参数进行了高度泛化,它能表示0到任意个数、任意类型的参数。 展开可变模版参数函数的方法一般有两种:一种是通过递归函数来展开参数包,另外一种是通过逗号表达式来展开参数包
    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;//线程池销毁的标志 
};
 
// the constructor just launches some amount of workers
inline ThreadPool::ThreadPool(size_t threads)
    :   stop(false)
{
    for(size_t i = 0;i<threads;++i)
        workers.emplace_back(
            [this]//函数体内可以使用 Lambda 所在类中的成员变量。 
            {
                for(;;)
                {
                    std::function<void()> task;//函数指针只能指向一个函数,而std::function对象可以代表任何可以调用的对象,比如说任何可以被当作函数一样调用的对象。

                    {
                        std::unique_lock<std::mutex> lock(this->queue_mutex);//上锁 
                        this->condition.wait(lock,
                            [this]{ return this->stop || !this->tasks.empty(); });//当前线程仅在pred=false时阻塞;如果pred=true时,不阻塞。
                            														//当任务队列为空同时stop为false是阻塞 
                        if(this->stop && this->tasks.empty())//当stop为true同时任务队列为空时结束 
                            return;
                        task = std::move(this->tasks.front());//取出任务 
                        this->tasks.pop();//从任务队列删除这个任务 
                    }

                    task();//执行任务 
                }
            }
        );
}

// add new work item to the pool
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;

	//指针类型 ,类型为 packaged_task<return_type()>
    auto task = std::make_shared< std::packaged_task<return_type()> >(//当std::packaged_task<>在一个单独的线程中运行时,它会调用相关的回调函数,并将返回值或异常存储在其内部共享状态中。该值可以通过std :: future <>对象在其他线程或主函数中访问。
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );
        
    std::future<return_type> res = task->get_future();//获取future对象,从packaged_task<>获取相关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_one();//入队一个任务,通知阻塞的线程 
    return res;
}

// the destructor joins all threads
inline ThreadPool::~ThreadPool()
{
    {
        std::unique_lock<std::mutex> lock(queue_mutex);
        stop = true;
    }
    condition.notify_all();
    for(std::thread &worker: workers)
        worker.join();//join()函数是一个等待线程完成函数,主线程需要等待子线程运行结束了才可以结束 
}

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

#include "ThreadPool.h"

int main()
{
    
    ThreadPool pool(4); //创建4个线程的线程池 
    /*

	
	*/
    std::vector< std::future<int> > results;// future通过get方法获取异步返回值

    for(int i = 0; i < 8; ++i) {
        results.emplace_back(              //vector中类似push_back的功能,emplace_back直接通过参数构造对象至容器中,不需要拷贝或者移动 
            pool.enqueue([i] 			//lambda匿名函数  [capture](parameters) mutable ->return-type{statement} (无参数)省略了返回值类型 
            								//将i按值进行传递。按值进行传递时,函数体内不能修改传递进来的 i拷贝,因为默认情况下函数是 const 的,要修改传递进来的拷贝,可以添加 mutable 修饰符。
										
			{								// 函数体实现 
                std::cout << "hello " << i << std::endl;
                std::this_thread::sleep_for(std::chrono::seconds(1));//chrono是c++ 11中的时间库,提供计时,时钟等功能。 
                std::cout << "world " << i << std::endl;
                return i*i;//->return-type-> 返回值类型:标识函数返回值的类型,当返回值为 void,或者函数体中只有一处 return 的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
            }
			
			)
        );
    }

    for(auto && result: results)//非常量右值引用 ,当vector返回临时对象,使用auto&会编译错误,临时对象不能绑在non-const l-value reference (非常量左值引用),需使用auto&&(非常量右值引用),初始化右值时也可捕获 
        std::cout << result.get() << ' '; //future通过get方法获取异步返回值
    std::cout << std::endl;
    
    return 0;
}

从main函数中可以看出线程池是预先构造几个线程,然后让每一个 thread 都去执行调度函数:循环获取一个 task,然后执行之。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值