我为什么要使用std :: async?

问题描述 投票:60回答:5

我正在尝试深入探索新C ++ 11标准的所有选项,同时使用std :: async并阅读其定义,我注意到两件事,至少在linux下使用gcc 4.8.1:

  • 它被称为异步,但它有一个真正的“顺序行为”,基本上在你调用与异步函数foo相关的未来的行中,程序会阻塞,直到执行foo它完成。
  • 它取决于与其他人完全相同的外部库,以及更好的非阻塞解决方案,这意味着pthread,如果你想使用std::async,你需要pthread。

在这一点上,我很自然地问为什么选择std :: async甚至是一组简单的仿函数?这是一个根本无法扩展的解决方案,您调用的未来越多,您的程序响应就越少。

我错过了什么吗?你能展示一个被授予以异步,非阻塞方式执行的例子吗?

c++ c++11 asynchronous future stdasync
5个回答
53
投票

如果您需要异步操作的结果,那么无论您使用哪个库,都必须阻止。这个想法是你可以选择何时阻止,并且希望当你这样做时,你可以阻止一段时间,因为所有的工作都已经完成了。

另请注意,std::async可以使用std::launch::asyncstd::launch::deferred策略启动。如果你没有指定它,允许实现选择,并且它可以选择使用延迟评估,这将导致在你尝试从未来获得结果时完成所有工作,从而导致更长的块。因此,如果您想确保工作是异步完成的,请使用std::launch::async


73
投票
  • 它被称为异步,但它有一个真正的“顺序行为”,

不,如果您使用std::launch::async策略,那么它将在新线程中异步运行。如果您未指定策略,则它可能在新线程中运行。

基本上在你调用与异步函数foo相关联的未来的行中,程序会阻塞,直到执行foo它已经完成。

它只会在foo未完成时阻塞,但如果它是异步运行的(例如因为你使用的是std::launch::async策略),它可能在你需要它之​​前完成了。

  • 它取决于与其他人完全相同的外部库,以及更好的非阻塞解决方案,这意味着pthread,如果你想使用std :: async,你需要pthread。

错了,它不必使用Pthreads实现(在Windows上它不是,它使用ConcRT功能。)

在这一点上,我很自然地问为什么选择std :: async甚至是一组简单的仿函数?

因为它保证了线程安全并在线程之间传播异常。你能用一套简单的仿函数做到吗?

这是一个根本无法扩展的解决方案,您调用的未来越多,您的程序响应就越少。

不必要。如果您未指定启动策略,那么智能实现可以决定是启动新线程,还是返回延迟函数,或者在有更多资源可用时返回稍后决定的内容。

现在,对于GCC的实现,如果你不提供启动策略然后使用当前版本,它将永远不会在新线程中运行(有一个bugzilla report),但这是该实现的属性,而不是std::async一般。您不应该将标准中的规范与特定实现混淆。阅读一个标准库的实现是学习C ++ 11的一种不好的方法。

你能展示一个被授予以异步,非阻塞方式执行的例子吗?

这不应该阻止:

auto fut = std::async(std::launch::async, doSomethingThatTakesTenSeconds);
auto result1 = doSomethingThatTakesTwentySeconds();
auto result2 = fut.get();

通过指定启动策略,您可以强制执行异步,如果您在执行时执行其他工作,则结果将在您需要时准备就绪。


12
投票

我认为你的问题是std::future说它阻止了get。它仅在结果尚未准备好时才会阻止。

如果您可以安排结果已经准备好,这不是问题。

有很多方法可以知道结果已经准备好了。您可以轮询future并询问它(相对简单),您可以使用锁或原子数据来传达它已准备好的事实,您可以构建一个框架,将“已完成”的future项目传递到消费者可以与之交互的队列中,你可以使用某种信号(一次阻止多个事物或轮询)。

或者,您可以完成本地可以完成的所有工作,然后阻止远程工作。

举个例子,想象一下并行递归合并排序。它将数组拆分为两个块,然后对一个块进行async排序,同时对另一个块进行排序。一旦对其一半进行排序,原始线程就无法继续进行,直到第二个任务完成。所以它做了.get()和块。一旦两个半部分已经排序,它就可以进行合并(理论上,合并也可以至少部分地并行完成)。

此任务的行为类似于在外部与之交互的线性任务 - 完成后,对数组进行排序。

然后我们可以将它包装在std::async任务中,并有一个future排序数组。如果我们想要,我们可以添加一个信号程序让我们知道future已经完成,但只有当我们有一个等待信号的线程时才有意义。


3
投票

referencehttp://en.cppreference.com/w/cpp/thread/async

如果设置了async标志(即policy&std :: launch :: async!= 0),则async在单独的执行线程上执行函数f,就好像由std :: thread(f,args ...)生成的一样。除了如果函数f返回一个值或抛出异常,它将存储在可通过std :: future访问的共享状态中,async返回给调用者。

保留异常记录是一个很好的属性。


0
投票

http://www.cplusplus.com/reference/future/async/

有三种政策,

  1. launch::async
  2. launch::deferred
  3. launch::async|launch::deferred

默认情况下,launch::async|launch::deferred被传递给std::async

© www.soinside.com 2019 - 2024. All rights reserved.