class ThreadPool
{
std::queue<std::function<void()>> _tasks; // 任务队列
std::vector<std::thread> _threadPool; // 线程池
int _cntThread; // 线程数
std::mutex _mutex;
std::condition_variable _cv;
std::atomic<bool> _stop;
public:
ThreadPool(int cntThread) : _cntThread(cntThread), _stop(false)
{
// 线程函数
auto ThreadFunc = [this]
{
while (true)
{
std::function<void()> task;
{
std::unique_lock<std::mutex> ul(_mutex);
_cv.wait(ul, [this] { return (!_tasks.empty() || _stop); });
if (_stop) break;
task = _tasks.front();
_tasks.pop();
}
// 必须在解锁后再调用
task();
}
{
std::lock_guard<std::mutex> ul(_mutex);
std::cout << "thread " << std::this_thread::get_id() << " exit!" << std::endl;
}
};
// 创建线程并启动
for (int i = 0; i < _cntThread; i++)
{
std::thread t(ThreadFunc);
_threadPool.push_back(std::move(t));
}
}
~ThreadPool()
{
_stop = true;
_cv.notify_all();
for (auto iter = _threadPool.begin(); iter != _threadPool.end(); iter++)
{
(*iter).join();
}
}
template <typename F, typename... Args>
auto commit(F f, Args... args) -> std::future<decltype(f(args...))>
{
using RetType = decltype(f(args...)); // 获取返回类型
std::shared_ptr<std::packaged_task<RetType()>> task = std::make_shared<std::packaged_task<RetType()> >(
std::bind(f, args...));
std::cout << "task 引用计数:" << task.use_count() << std::endl;
std::future<RetType> ft = task->get_future();
{
std::lock_guard<std::mutex> lg(_mutex);
_tasks.push([task] { (*task)(); });
}
std::cout << "task 引用计数:" << task.use_count() << std::endl;
_cv.notify_one();
return ft;
}
};
细节
std::shared_ptr<std::packaged_task<RetType()>> task = std::make_shared<std::packaged_task<RetType()> >(
std::bind(f, args...));
1、std::bind
利用函数适配器std::bind把实参绑定到输出函数,把真正的任务函数改造成无参函数。目的是线程内部对于任何任务,都可以用相同的调用方式来启动任务,同时无需管理传参。
2、std::packaged_task
std::packaged_task把任务及其未来的执行结果封装起来,方便任务提交者获取任务的执行情况。如果对任务的执行结果不感兴趣,可以去掉这层封装,也就不用返回std::future对象,甚至不用智能指针std::shared_ptr。
template <typename F, typename... Args>
void commit(F f, Args... args)
{
auto task = std::bind(f, args...);
{
std::lock_guard<std::mutex> lg(_mutex);
_tasks.push([task] { (task)(); });
}
_cv.notify_one();
}
3、std::shared_ptr
如果这里不用智能指针,task变量超出生命周期后就会被析构,会在其关联的shared state中存储一个future_error异常,错误码是broken_promise。当工作者线程取出任务并执行完成,需要把执行结果存储在shared state中时就会抛出future_error异常,错误码是promise_already_satisfied。