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,然后执行之。