C ++ Common std :: make_unique,std :: packaged_task和std :: promise问题

问题描述 投票:1回答:1

问题创建调度程序时,函数对象的最后一次复制或移动是函数对象的最后一个位置(由工作线程引用)。如果你使用std :: function在调度程序中存储函数,那么任何std :: promises或std :: packaged_task或其他类似的仅移动类型都不起作用,因为它们不能被std :: function复制。

类似地,如果您在调度程序中使用std :: packaged_task,则会产生不必要的开销,因为许多任务根本不需要打包任务返回的std :: future。

常见且不太好的解决方案是使用std :: shared_ptr <std :: promise>或std :: shared_ptr <std :: packaged_task>,但它会产生很大的开销。

解决方案A make_owner,类似于具有一个键差异的make_unique,移动或复制只是转移对象的销毁控制。它与std :: unique_ptr基本相同,只是它是可复制的(它基本上总是移动,即使在副本上也是如此)。 Grosss ....

这意味着移动std :: functions不需要std :: shared_ptr的副本,这需要引用计数,这也意味着引用计数等的开销明显减少。需要一个指向该对象的单个原子指针。移动或复制将转移控制。主要区别在于副本也会转移控制权,这在严格的语言规则方面可能有点禁忌,但我没有看到另一种解决方法。

此解决方案很糟糕,因为:

  • 它忽略了复制语义。
  • 它抛弃了const(在复制构造函数和operator =中)

Grrr如果有人知道另一种方法可以避免使用共享指针或者只是在调度程序中使用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();

以及packaged_task的示例

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不适合我的应用程序。

c++ c++17 scheduler
1个回答
1
投票

主要问题是:为什么要在将lambda传递给std::function构造函数之前用std::thread包装lambda?

这样做非常好:

std::thread runner([prom = std::move(proms)]() mutable noexcept
{
    prom->set_value(80085);
});

你可以找到为什么std::function不允许你存储一个只移动的lambda here的解释。

如果你打算用包裹的lambda传递std::function到某个函数,而不是:

void foo(std::function<void()> f)
{
    std::thread runner(std::move(f));
    /* ... */
}

foo(std::function<void()>([](){}));

你可以这样做:

void foo(std::thread runner)
{
    /* ... */
}

foo(std::thread([](){}));

更新:它可以以老式的方式完成。

std::thread runner([prom_deleter = proms.get_deleter(), prom = proms.release()]() mutable noexcept
{
    prom->set_value(80085);
    // if `proms` deleter is of a `default_deleter` type
    // the next line can be simplified to `delete prom;`
    prom_deleter(prom);
});
© www.soinside.com 2019 - 2024. All rights reserved.