正确使用std :: promise和std :: future,segfaults

时间:2017-11-15 14:30:44

标签: c++ multithreading c++14 threadpool

我一直在小线程池实现上试试运气。 然而,在概念化和实施之后,我已经碰到了一堵砖墙。 我已经确认工作线程正在启​​动并正确睡眠,并且它们正确地接收并执行存储的任务。 但是,我的程序段错误 - 我非常确定它在promise.set_value

我不确定如何提供一个完整的,可验证的示例(鉴于我几乎无法上传整个代码),但我会包含细分 我认为与这个问题有关。 首先,工人的创建方式如下:

worker = [this](){
    while(true)
    {
        std::unique_lock<std::mutex> lock(mStatusMutex); //CV for status updates
        mCV.wait(lock);
        if(mStatus != Running) //If threadpool status does not imply running
            break; //Break out of loop, ending thread in the process
        else //If threadpool is in running state
        {
            lock.unlock(); //Unlock state
            while(true) //Loop until no tasks are left
            {
                mTasksMutex.lock(); //Lock task queue
                if(mTasks.empty()) //IF no tasks left, break out of loop and return to waiting
                {
                    mTasksMutex.unlock();
                    break;
                }
                else //Else, retrieve a task, unlock the task queue and execute the task
                {
                    std::function<void()> task = mTasks.front();
                    mTasks.pop();
                    mTasksMutex.unlock();
                    task(); //Execute task
                }
            }
        }
    }
};

然后开始并存储到这样的std::vector<std::thread>

std::thread tWorker(worker);
mWorkers.push_back(std::move(tWorker));

现在,我认为以下的棘手部分是在任务队列中添加/执行任务时,std::queue<std::function<void()>>。 以下两个函数与此相关:

template<typename RT>
inline std::future<RT> queueTask(std::function<RT()> _task, bool _execute = false)
{
    std::promise<RT> promise;
    std::function<void()> func([&_task, &promise]() -> RT {
        RT val = _task();
        promise.set_value(val);
    });

    mTasksMutex.lock();
    mTasks.emplace(func);
    mTasksMutex.unlock();
    if(_execute) flush();
    return promise.get_future();
}
inline void flush()
{
    mCV.notify_all();
}

这种方法有什么问题吗? 对于那些认为这是一个糟糕问题的人,请随时告诉我如何改进它。 完整代码托管在my github repo

1 个答案:

答案 0 :(得分:2)

主要问题是承诺已经死了。完成queueTask后,promise将被销毁,此任务现在只有一个悬空引用。任务必须共享promise的所有权,以便它能够活得足够长,以实现它。

基础std::function对象_task也是如此,因为您通过引用捕获它。

您正在使用std::function,这需要可复制的对象,因此...... shared_ptr

template<typename RT>
inline std::future<RT> queueTask(std::function<RT()> _task, bool _execute = false)
{
    auto promise = std::make_shared<std::promise<RT>>();
    std::function<void()> func([promise, task=std::move(_task)]{
        RT val = _task();
        promise->set_value(val);
    });

    {
        std::lock_guard<std::mutex> lk(mTasksMutex); // NB: no manual lock()/unlock()!!
        mTasks.emplace(func);
    }
    if(_execute) flush();
    return promise->get_future();
}

请考虑std::packaged_task