//案例是4个线程,8个任务
1)线程池头文件
#ifndef THREAD_POOL_H
#define THREAD_POOL_H
#include<vector>
#include<functional>
#include<queue>
#include<condition_variable>
#include<thread>
#include<future>
#include<memory>
#include<stdexcept>
//共计3个函数,5个变量;
class ThreadPool
{
public:
ThreadPool(size_t);
template<class F,class... Args>
auto enqueue(F&& f,Args&&... args)
->std::future<typename std::result_of<F(Args...)>::type>;
~ThreadPool();
private:
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
std::mutex queue_mutex;
std::condition_variable condition;
bool stop;
};
inline ThreadPool::ThreadPool(size_t threads):stop(false)
{
for(size_t i = 0; i < threads; ++i)
{
//利用两个lambda表达式
workers.emplace_back(
[this]
{
for(;;)
{
std::function<void()> task;
{ //这个大括号主要是为了加锁设置的作用域权限
std::unique_lock<std::mutex> lock(this->queue_mutex);
//即其表示若线程池已停止或者任务队列中不为空,则不会进入到wait状态;
//说白了就是任务队列中有任务,就不能等待!!!
this->condition.wait(lock, [this]{return this->stop || !this->tasks.empty();} ); //可简写returnstop || !tasks.empty();
if(stop && tasks.empty()) //若线程池已经停止且任务队列为空,则线程返回,没必要进行死循环
{
return;
}
task = std::move(this->tasks.front());
this->tasks.pop();
}
task(); //任务运行起来
}
}
);
}
}
template<class F,class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args)
->std::future<typename std::result_of<F(Args...)>::type> // ->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);
if(stop)
{
throw std::runtime_error("enqueue on stopped threadpool");
}
tasks.emplace([task](){(*task)();});
condition.notify_one();
return res; //最终返回的是放在std::future中的F(Args…)返回类型的异步执行结果
}
}
inline ThreadPool::~ThreadPool()
{
{
std::unique_lock<std::mutex> lock(queue_mutex);
stop = true;
}
condition.notify_all();
for(std::thread& worker : workers)
{
worker.join();
}
}
#endif
2)线程池测试文件
#include <iostream>
#include <vector>
#include <chrono>
#include "threadpool.h"
using namespace std;
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<<endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout<<"world---: "<<i<<endl;
return i*i;
}
)
);
}
for(auto&& result : results)
{
std::cout<<"result is: "<<result.get()<<endl;
}
std::cout<<std::endl;
return 0;
}
三)对上面代码的分析
3个成员函数
ThreadPool(size_t) 线程池的构造函数
auto enqueue(F&& f, Args&&… args) 将任务添加到线程池的任务队列中
~ThreadPool() 线程池的析构函数
5个成员变量
std::vector< std::thread > workers 用于存放线程的数组,用vector容器保存
std::queue< std::function<void()> > tasks 用于存放任务的队列,用queue队列进行保存。任务类型为std::function<void()>。
因为std::function是通用多态函数封装器,也就是说本质上任务队列中存放的是一个个函数
std::mutex queue_mutex 一个访问任务队列的互斥锁,在插入任务或者线程取出任务都需要借助互斥锁进行安全访问
std::condition_variable condition 一个用于通知线程任务队列状态的条件变量,若有任务则通知线程可以执行,否则进入wait状态
bool stop 标识线程池的状态,用于构造与析构中对线程池状态的了解;
四)对任务队列及线程池的分析
由于刚开始创建线程池,线程池表示未停止,且任务队列为空,所以每个线程都会进入到wait状态;