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_task
的 operator()
的指针保存对 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 中比复制它更有效。
我正在分析以下代码片段并试图详细理解它:
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_task
的 operator()
的指针保存对 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 中比复制它更有效。