【问题标题】:Why does boost::future<T>::then() spawn a new thread?为什么 boost::future<T>::then() 会产生一个新线程?
【发布时间】:2014-04-08 10:42:16
【问题描述】:

将延续附加到boost::future 时,延续在新线程中执行:

std::cout << "main: " << boost::this_thread::get_id() << std::endl;

boost::promise<void> p;
boost::future<void> f = p.get_future();
p.set_value();

boost::future<void> f2 = f.then([] (boost::future<void>) {
    std::cout << "future: " << boost::this_thread::get_id() << std::endl;        
});

这个 sn-p 输出:

main: 0x7fff7a8d7310
future: 0x101781000

为什么.then() 允许这样做,更重要的是,有没有办法自定义这种行为?从promise/packaged_task/async 返回的futures 行为是否不同?

【问题讨论】:

  • 我想 .then() 在这方面就像 async() 。是的,从异步返回的期货与直接从承诺(或打包任务)获得的期货之间存在细微差别。我认为是 Scott Meyer 最详细地描述了这一点(实际上认为这是当前标准的一个坏属性)。那里还应该有 boost::launch::deferred/async 参数,比如 async

标签: c++ multithreading c++11 boost


【解决方案1】:

@ikh 回答中已经提到过。但为了更清楚,这里是对 OP 问题的直接答案。

boost::future continuation 应该在新线程还是在调用线程中执行是可定制的。

boost::launch 策略参数指定延续应该如何运行。见这里:Enumeration launch

enum class launch
{
    none = unspecified,
    async = unspecified,
    deferred = unspecified,
    executor = unspecified,
    inherit = unspecified,
    any = async | deferred
};

async(launch::deferred, ...)::then(launch::deferred, ...) 创建的未来已关联启动策略 launch::deferred

所以,试试运行这个:

boost::future<void> f2 = f.then(
    boost::launch::deferred,
    [] (boost::future<void>&&) {
        std::cout << "future: " << boost::this_thread::get_id() << std::endl;        
    }
);

这应该在同一个线程中运行延续。
在 Windows 平台上使用 boost 1.61 + Visual Studio 2015 进行测试。

【讨论】:

  • 如何在默认情况下(全局)在同一线程上运行延续而不在每次调用中添加此启动策略?
  • 我通过在boost::future 周围编写简单的包装来做到这一点。它通过显式传递boost::launch::deferred 策略来装饰boost::future::then() 方法调用。
  • 好的,我明白了。我正在维护一个库,并且按照您的建议进行操作将意味着该库的所有用户都必须使用包装好的未来事物,而不是简单的提升。这也不好看。无论如何,你能把你的包装贴在某个地方吗?感谢您的评论!
【解决方案2】:

产生一个新线程是合理的。看看这段代码:

std::cout << "main: " << boost::this_thread::get_id() << std::endl;

boost::promise<void> p;
boost::future<void> f = p.get_future();
p.set_value();

boost::future<void> f2 = f.then([] (boost::future<void>) {
    SomeVeryVeryVeryLongLongLongLongTask();

    std::cout << "future: " << boost::this_thread::get_id() << std::endl;        
});

在不产生新线程的情况下,我们必须在f.then(... 处等待SomeVeryVeryVeryLongLongLongLongTask()


如果您想查看某些证据?在reference

  • 如果父级是使用 promise(没有关联的启动策略)创建的,则延续的行为与使用策略参数 launch::async | 的第三个重载相同。 launch::deferred 和 func 的参数相同。

如您所知,如果我们使用launch::async | launch::deferred,我们不知道是生成了一个新线程还是只是延迟了它。但主要是产生了一个新线程,对吧?

PS。嗯?参考说“...promisepromise<> 打错了>o

PS2。请参阅@sehe 的回复。严格来说,他是对的。

【讨论】:

  • 就是这么定义的。可以使继续在与前一个任务相同的线程上执行。产生线程的预期较少并且有缺点。一般来说,我认为问题归结为:是否提升异步/然后允许控制线程调度?(您的答案没有解决这个问题)
  • 噢。该编辑非常有用。但最后一点“但主要是产生了一个新线程,对吧?”不必要地扼杀了客观性价值。但是,是的,这意味着:你不能在这里控制线程调度。
  • @sehe 严格来说,你是对的。但是我想轻松一点...无论是生成新线程还是仅在另一个线程上运行任务,它都有类似的效果..
  • 除非您可以控制线程/调度的数量。想象一下boost::async::set_thread_pool_size(1)
  • 我不认为 OP 是在统计或概率之后。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-19
  • 2012-11-30
  • 1970-01-01
  • 2015-05-26
  • 2012-05-02
  • 2023-02-23
  • 2016-06-10
相关资源
最近更新 更多