代码之家  ›  专栏  ›  技术社区  ›  tubberd

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

  •  1
  • tubberd  · 技术社区  · 7 年前

    我一直在一个小的线程池实现上碰碰运气。 然而,在概念化和实现之后,我遇到了困难。 我已经确认工作线程能够正确启动和睡眠,并且能够正确地拾取和执行存储的任务。 然而,我的程序出错了-我很确定它在 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 回复  |  直到 7 年前
        1
  •  2
  •   Barry    7 年前

    主要问题是,这一承诺已经失效。什么时候 queueTask 完成后 promise 被销毁,任务现在只有一个悬而未决的引用。任务必须共享 许诺 为了让它活得足够长以实现它。

    底层也是如此 std::function 对象 _task ,因为您是通过引用来捕获它的。

    您正在使用 std::函数 ,这需要可复制的对象,因此。。。 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 相反