std::packaged_task 的工作原理

How std::packaged_task works

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

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;
}

关于 std::packaged_task 我几乎没有什么问题。

正如您在 add(...) 方法体中看到的那样, std::packaged_task - task 的实例是局部变量,其作用域在方法执行结束时结束。 std::future 类型的 return 值 ret 是 return 复制的。 ret 的值是从 task(本地)中给出的。因此,一旦方法执行完成,task 超出范围,因此 我预计 正在 returned 连接的 std::future 实例变得无效,我的理解正确吗?

在方法执行期间,线程中要执行的任务方法被放置到std::queue<Job> jobs中。为什么只有指向 std::packaged_taskoperator() 的指针保存对 Function 的引用作为方法参数保存在 std::queue 中?我希望直接存储 std::packaged_task 以保存对正在创建的实例的引用...?

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

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

So once the method execution is finished, the task goes out of scope and so I expect the connected std::future instance being returned becomes invalid, is my understanding correct?

task 是一个 std::shared_ptr,它被复制到传递给 jobs 的闭包中——只要工作存在,它就会一直存在。

So once the method execution is finished, the task goes out of scope and so I expect the connected std::future instance being returned becomes invalid, is my understanding correct?

没有

future 是共享状态的句柄。这种状态是承诺和未来共享的。

将其视为将一种特殊的 shared_ptr 封装到管理 promise/future 关系状态的(不透明)对象。

As you can see in the add(...) method body, the instance of std::packaged_task - task is local variable which scope ends with the end of the method execution.

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

The return value ret of std::future type is returned by copy.

不完全是。 ret 更有可能由 move 而不是复制返回。

the task goes out of scope and so I expect the connected std::future instance being returned becomes invalid, is my understanding correct?

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

I would expect to store directly the std::packaged_task in order to hold the reference to the instance being created...?

没错,它可以直接存储 std::packaged_task。我的猜测是这个库的作者不想弄乱 uncopiable/unmovable 仿函数。如果 std::bind 的结果是不可复制的 并且 是不可移动的,那么你不能真正将 std::packaged_task 移动到队列中。使用共享指针可以解决此问题,因为您不 copy/move packaged_task 本身,只有指针。然而,您可以将任务对象直接构建到队列中,但是在持有锁的情况下进行构建并不是一个好的做法。

不过我同意,也许将 task 移动到 lambda 中比复制它更有效。