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事件中拾取最后一个事件的任何线程通知它