std :: packaged_task如何工作

Mar*_*cký 5 c++ multithreading future packaged-task c++11

我正在分析以下代码片段,并试图详细了解它:

template<typename FUNCTION, typename... ARGUMENTS>
auto ThreadPool::add( FUNCTION&& Function, ARGUMENTS&&... Arguments ) -> std::future<typename std::result_of<FUNCTION(ARGUMENTS...)>::type>
{
    using PackedTask = std::packaged_task<typename std::result_of<FUNCTION(ARGUMENTS...)>::type()>;

    auto task = std::make_shared<PackedTask>(std::bind(std::forward<FUNCTION>(Function), std::forward<ARGUMENTS>(Arguments)...));

    // get the future to return later
    auto ret = task->get_future();

    {
        std::lock_guard<std::mutex> lock{jobsMutex};
        jobs.emplace([task]() { (*task)(); });
    }

    // let a waiting thread know there is an available job
    jobsAvailable.notify_one();

    return ret;
}
Run Code Online (Sandbox Code Playgroud)

我对此几乎没有任何疑问std::packaged_task.

正如可以在看到add(...)方法体,实例std::packaged_task- task是范围与方法执行结束时结束局部变量.返回值retstd::future类型由复制返回.的值ret被给予了的task(这是本地).所以一旦方法执行完成,task超出范围,所以我希望返回的连接的std :: future实例变得无效,我的理解是否正确?

在执行该方法期间,将在线程中执行的任务方法被置于其中std::queue<Job> jobs.为什么只有指针operator()std::packaged_task持有的参考Function在举行给出方法参数std::queue?我希望直接存储std::packaged_task以保存对正在创建的实例的引用...?

无论如何,源代码片段来自ThreadPool实现,可以在这里找到https://github.com/dabbertorres/ThreadPool,似乎完全正常工作.所以我认为这是正确的,但遗憾的是我并不完全清楚...如果有人能够解释这些东西的运作方式,我会很高兴的...

非常感谢愿意提供帮助的任何人.干杯马丁

Dav*_*aim 4

正如您在 add(...) 方法体中看到的, std::packaged_task - task 的实例是局部变量,其作用域随着方法执行结束而结束。

是的,它是 的局部变量std::shared_ptr<PackedTask>。当它超出范围时,它将引用计数减 1。如果新的引用计数为 0,它将删除它指向的对象。幸运的是,jobs持有该共享指针的副本,以便指向的对象保持活动状态。

std::future类型的返回值ret是通过copy返回的。

不完全是。更有可能是ret通过移动而不是通过复制返回。

该任务超出了范围,因此我希望返回的连接的 std::future 实例变得无效,我的理解正确吗?

再次,task是共享指针。队列jobs使其保持活动状态,并可能ret保存该共享指针的另一个副本(以实际提取 的结果task)。因此,只要任务在队列中并且有人持有该结果的未来,任何事情都不会变得无效。

我希望直接存储 std::packaged_task 以便保存对正在创建的实例的引用......?

确实,它可以直接存储std::packaged_task。我的猜测是这个库的作者不想弄乱不可复制/不可移动的函子。如果 的结果std::bind不可复制不可移动,则您无法真正将其移动std::packaged_task到队列中。使用共享指针解决了这个问题,因为您不复制/移动 packaged_task 本身,而只复制/移动指针。但是,您可以将任务对象直接构建到队列中,但在持有锁下执行此操作并不是一个好的做法。

不过,我确实同意,也许迁移task到 lambda 比复制它更有效。