源代码
https://github.com/progschj/ThreadPool.git
这是适应于c++11版本以上的线程池实现,可以添加各种类型线程任务,同时由于使用std::future等新功能,从而能够获得任务的返回值。在github上有3.2k的star。
最后有疑问,求大佬解惑(已经理解了,切换任务并非时刻进行,若上一个任务正在进行,则代码运行在task(),此时不会切换任务)
线程池雏形
c++线程池有一个比较基本的模型,我看了一些工程,发现它们的基本构造都可以从这篇博客中理解。
https://www.cnblogs.com/ailumiyana/p/10016965.html
简要叙述线程池原理
线程池通常实现为一个类ThreadPool。该类主要包含4个功能:
1. 线程池管理器(构造函数,让当前空闲进程取队列的头任务运行; 析构函数,等待线程结束)
2. 工作线程(vector<thread>)
3. 任务接口(addTask或enqueue)
4. 任务队列( 一般使用queue)
关键点:
1. 建立线程池时,指定一定数量的线程,开启,每个线程的任务就是当队列不为空时且当前没有运行任务时,取任务并运行(通过条件互斥量实现)
2. 每当有任务来时,存入任务队列,并通知每个线程(通过条件互斥量实现)
涉及到的c++11特性
1. 匿名函数 比较简单
2. 可变模板参数
3. std::bind
4. std::future、std::packaged_task
5. std::function
- 其中235是重点,std::function可以用于函数指针, std::function+std::bind可以实现将一个和function声明时不同的函数绑在一起
- std::bind+可变参数模板可以实现任意函数都能绑定到function上。
举例:
template<class F, class... Args>
int push(F&& f, Args&&... args)
{
std::function<void()> task;
task = std::bind(f, args...);
task();
return 1;
}
std::future则是用于获得线程函数的返回值,通过get()方法可以实现。
代码
代码总共分两部分:ThreadPool.h和example.cpp。虽然还有一些c++特性,但大体上可以忽略那些无关紧要的特性了(forward)
ThreadPool.h中部分解释:
// 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(
//这下面按道理是一个thread的构造函数,因为emplace_back(thread)。
//thread t1(这个匿名函数)
//这个匿名函数作用就是取出队列的东西运行
[this]
{
for(;;)
{
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(this->queue_mutex);
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就是之前存放的匿名函数,执行这个匿名函数就是在执行任务
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;
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_one();
return res;
}
#endif