C++ 线程池:阻止销毁,直到所有工作完成
我有以下线程池实现:C++ 线程池:阻止销毁,直到所有工作完成,c++,multithreading,threadpool,c++14,C++,Multithreading,Threadpool,C++14,我有以下线程池实现: template<typename... event_args> class thread_pool{ public: using handler_type = std::function<void(event_args...)>; thread_pool(handler_type&& handler, std::size_t N = 4, bool finish_before_exit = true) : _ha
template<typename... event_args>
class thread_pool{
public:
using handler_type = std::function<void(event_args...)>;
thread_pool(handler_type&& handler, std::size_t N = 4, bool finish_before_exit = true) : _handler(std::forward<handler_type&&>(handler)),_workers(N),_running(true),_finish_work_before_exit(finish_before_exit)
{
for(auto&& worker: _workers)
{
//worker function
worker = std::thread([this]()
{
while (_running)
{
//wait for work
std::unique_lock<std::mutex> _lk{_wait_mutex};
_cv.wait(_lk, [this]{
return !_events.empty() || !_running;
});
//_lk unlocked
//check to see why we woke up
if (!_events.empty()) {//was it new work
std::unique_lock<std::mutex> _readlk(_queue_mutex);
auto data = _events.front();
_events.pop();
_readlk.unlock();
invoke(std::move(_handler), std::move(data));
_cv.notify_all();
}else if(!_running){//was it a signal to exit
break;
}
//or was it spurious and we should just ignore it
}
});
//end worker function
}
}
~thread_pool()
{
if(_finish_work_before_exit)
{//block destruction until all work is done
std::condition_variable _work_remains;
std::mutex _wr;
std::unique_lock<std::mutex> lk{_wr};
_work_remains.wait(lk,[this](){
return _events.empty();
});
}
_running=false;
//let all workers know to exit
_cv.notify_all();
//attempt to join all workers
for(auto&& _worker: _workers)
{
if(_worker.joinable())
{
_worker.join();
}
}
}
handler_type& handler()
{
return _handler;
}
void propagate(event_args&&... args)
{
//lock before push
std::unique_lock<std::mutex> _lk(_queue_mutex);
{
_events.emplace(std::make_tuple(args...));
}
_lk.unlock();//explicit unlock
_cv.notify_one();//let worker know that data is available
}
private:
bool _finish_work_before_exit;
handler_type _handler;
std::queue<std::tuple<event_args...>> _events;
std::vector<std::thread> _workers;
std::atomic_bool _running;
std::condition_variable _cv;
std::mutex _wait_mutex;
std::mutex _queue_mutex;
//helpers used to unpack tuple into function call
template<typename Func, typename Tuple, std::size_t... I>
auto invoke_(Func&& func, Tuple&& t, std::index_sequence<I...>)
{
return func(std::get<I>(std::forward<Tuple&&>(t))...);
}
template<typename Func, typename Tuple, typename Indicies = std::make_index_sequence<std::tuple_size<Tuple>::value>>
auto invoke(Func&& func, Tuple&& t)
{
return invoke_(std::forward<Func&&>(func), std::forward<Tuple&&>(t), Indicies());
}
};
模板
类线程池{
公众:
使用handler_type=std::function;
线程池(处理程序类型和处理程序,std::size\t N=4,bool finish\u before\u exit=true):\u处理程序(std::forward(处理程序)),\u workers(N),\u running(true),\u finish\u work\u before\u exit(在退出前完成)
{
对于(自动和工作人员:\工作人员)
{
//工人职能
worker=std::thread([this]()
{
当(_运行时)
{
//等待工作
std::unique_lock{u wait_mutex};
_cv.等待(_lk,[此]{
return!_events.empty()| |!_running;
});
//_lk解锁
//看看我们为什么醒来
如果(!\u events.empty()){//是新作品吗
std::unique_lock_readlk(_queue_mutex);
自动数据=_events.front();
_events.pop();
_readlk.unlock();
调用(std::move(_handler),std::move(data));
_cv.通知所有人();
}否则如果(!\u正在运行){//这是退出的信号吗
打破
}
//或者这是假的,我们应该忽略它
}
});
//终端工作函数
}
}
~thread_pool()
{
如果(退出前完成工作)
{//阻止销毁,直到所有工作完成
std::条件变量工作剩余;
std::mutex wr;
std::unique_lock lk{{u wr};
_还有工作要做。等等(lk,[这个](){
返回_events.empty();
});
}
_运行=错误;
//让所有工人都知道退出
_cv.通知所有人();
//尝试加入所有工人
对于(自动和/或工作人员:\工作人员)
{
if(_worker.joinable())
{
_worker.join();
}
}
}
处理程序类型&handler()
{
返回处理程序;
}
无效传播(事件参数&&…参数)
{
//先锁后推
std::unique_lock_lk(_queue_mutex);
{
_事件放置(std::make_tuple(args…);
}
_lk.unlock();//显式解锁
_cv.notify_one();//让工人知道数据可用
}
私人:
在退出前完成工作;
处理程序\类型\处理程序;
std::队列事件;
性病:病媒工作者;
std::原子布尔运行;
std::条件变量cv;
std::mutex\u wait\u mutex;
std::mutex\u queue\u mutex;
//用于将元组解压到函数调用中的帮助程序
模板
自动调用(Func&&Func,元组&&t,std::index_序列)
{
返回函数(std::get(std::forward(t))…);
}
模板
自动调用(Func&&Func、元组&&t)
{
返回invoke(std::forward(func)、std::forward(t)、Indicies());
}
};
我最近将此部分添加到析构函数中:
if(_finish_work_before_exit)
{//block destruction until all work is done
std::condition_variable _work_remains;
std::mutex _wr;
std::unique_lock<std::mutex> lk{_wr};
_work_remains.wait(lk,[this](){
return _events.empty();
});
}
if(退出前完成工作)
{//阻止销毁,直到所有工作完成
std::条件变量工作剩余;
std::mutex wr;
std::unique_lock lk{{u wr};
_还有工作要做。等等(lk,[这个](){
返回_events.empty();
});
}
目的是让析构函数阻塞,直到工作队列被完全消耗
但这似乎使程序陷入僵局。所有的工作都完成了,但等待似乎并没有在工作完成时结束
考虑这个例子:
std::mutex writemtx;
thread_pool<int> pool{
[&](int i){
std::unique_lock<std::mutex> lk{writemtx};
std::cout<<i<<" : "<<std::this_thread::get_id()<<std::endl;
},
8//threads
};
for (int i=0; i<8192; ++i) {
pool.propagate(std::move(i));
}
std::mutexwritemtx;
线程池{
[&](国际一){
std::unique_lock lk{writemtx};
std::cout代码死锁的原因是\u work\u remains
是一个条件变量,代码的任何部分都不会“通知”它。您需要将其作为类属性,并让从\u事件中拾取最后一个事件的任何线程通知它