【发布时间】:2018-09-01 05:06:13
【问题描述】:
问题 创建调度程序时,函数对象的最后一个副本或移动是函数对象被引用的最后一个位置(由工作线程)。如果您要使用 std::function 在调度程序中存储函数,那么任何 std::promises 或 std::packaged_task 或其他类似的仅移动类型都不起作用,因为它们不能被 std::function 复制。
同样,如果您要在调度程序中使用 std::packaged_task 它会带来不必要的开销,因为许多任务根本不需要打包任务返回的 std::future。
常见但不是很好的解决方案是使用 std::shared_ptrstd::promise> 或 std::shared_ptrstd::packaged_task>工作,但它会带来相当多的开销。
解决方案 make_owner 与 make_unique 类似,但有一个关键区别,移动 OR 复制只是转移了对对象销毁的控制。它基本上与 std::unique_ptr 相同,只是它是可复制的(它基本上总是移动,即使在副本上也是如此)。毛……
这意味着移动 std::functions 不需要 std::shared_ptr 的副本,这需要引用计数,这也意味着引用计数等的开销显着减少。指向对象的单个原子指针将需要,移动 OR 副本将转移控制权。主要区别在于副本也会转移控制权,这在严格的语言规则方面可能有点禁忌,但我看不出有其他方法。
这个解决方案不好,因为:
- 它忽略复制语义。
- 它抛弃了 const(在复制构造函数和运算符 = 中)
咕噜 它不像我想要的那样好的解决方案,所以如果有人知道另一种方法来避免使用共享指针或只在调度程序中使用 packaged_tasks,我很乐意听到它,因为我很难过......
我对这个解决方案很不满意......有什么想法吗? 我能够使用移动语义重新实现 std::function 但这似乎是一个巨大的痛苦,并且它在对象生命周期方面有其自身的问题(但在使用带有引用捕获的 std::function 时它们已经存在)。
问题的一些例子:
编辑 请注意,在目标应用程序中,我不能执行 std::thread a (std::move(a)),因为调度程序线程始终在运行,最多它们处于睡眠状态,从不加入,从不停止。线程池中有固定数量的线程,我无法为每个任务创建线程。
auto proms = std::make_unique<std::promise<int>>();
auto future = proms->get_future();
std::thread runner(std::move(std::function( [prom = std::move(proms)]() mutable noexcept
{
prom->set_value(80085);
})));
std::cout << future.get() << std::endl;
std::cin.get();
还有一个打包任务的例子
auto pack = std::packaged_task<int(void)>
( []
{
return 1;
});
auto future = pack.get_future();
std::thread runner(std::move(std::function( [pack = std::move(pack)]() mutable noexcept
{
pack();
})));
std::cout << future.get() << std::endl;
std::cin.get();
编辑
我需要从调度程序的上下文中执行此操作,我将无法移动到线程。
请注意,以上是最低可重现性,std::async 不适合我的应用程序。
【问题讨论】:
-
也许是部分重叠,但这个问题更广泛,我在询问调度程序上下文中的潜在解决方案或替代方案。