82

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

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

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

我错过了什么吗?您能否展示一个允许以异步、非阻塞方式执行的示例?

4

5 回答 5

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

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

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

它仅在 foo 未完成时阻塞,但如果它是异步运行的(例如,因为您使用std::launch::async策略),它可能在您需要它之前就已经完成。

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

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

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

因为它保证了线程安全并跨线程传播异常。你能用一组简单的函子做到这一点吗?

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

不必要。如果您没有指定启动策略,那么智能实现可以决定是否启动一个新线程,或者返回一个延迟函数,或者返回稍后决定的东西,当更多资源可用时。

现在,确实对于 GCC 的实现,如果您不提供启动策略,那么对于当前版本,它将永远不会在新线程中运行(有一个bugzilla 报告),但这是该实现的属性,而不是std::async一般. 您不应将标准中的规范与特定实现混淆。阅读一个标准库的实现是了解 C++11 的一种糟糕方式。

您能否展示一个允许以异步、非阻塞方式执行的示例?

这不应该阻止:

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

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

于 2013-07-31T14:57:09.883 回答
72

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

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

于 2013-07-31T06:36:29.090 回答
16

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

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

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

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

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

对于那些在外部与之交互的人来说,这个任务的行为就像一个线性任务——当它完成时,数组就会被排序。

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

于 2013-07-31T13:56:50.377 回答
3

参考http ://en.cppreference.com/w/cpp/thread/async

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

记录抛出的异常是一个很好的属性。

于 2013-07-31T06:38:20.727 回答
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.

于 2018-12-23T21:09:22.550 回答