为什么要使用std: async?

时间:2021-06-30 23:37:41

I'm trying to explore all the options of the new C++11 standard in depth, while using std::async and reading its definition, I noticed 2 things, at least under linux with gcc 4.8.1 :

我正在深入研究新的c++ 11标准的所有选项,在使用std::async并阅读它的定义时,我注意到两件事,至少在使用gcc 4.8.1的linux下是这样的:

  • it's called async, but it got a really "sequential behaviour", basically in the row where you call the future associated with your async function foo, the program blocks until the execution of foo it's completed.
  • 它被称为async,但它有一个真正的“序列行为”,基本上是在你调用与异步函数foo相关的未来的行中,程序块直到foo的执行完成为止。
  • it depends on the exact same external library as others, and better, non-blocking solutions, which means pthread, if you want to use std::async you need pthread.
  • 它依赖于与其他库完全相同的外部库,以及更好的非阻塞解决方案(即pthread),如果您希望使用std:::async,则需要pthread。

at this point it's natural for me asking why choosing std::async over even a simple set of functors ? It's a solution that doesn't even scale at all, the more future you call, the less responsive your program will be.

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

Am I missing something ? Can you show an example that is granted to be executed in an async, non blocking, way ?

我是不是漏掉了什么?您能展示一个被授予以异步、非阻塞方式执行的示例吗?

4 个解决方案

#1


46  

If you need the result of an asynchronous operation, then you have to block, no matter what library you use. The idea is that you get to choose when to block, and, hopefully when you do that, you block for a negligible time because all the work has already been done.

如果您需要异步操作的结果,那么无论您使用什么库,都必须阻塞。它的意思是你可以选择什么时候阻塞,希望当你这样做的时候,你可以阻塞一段可以忽略的时间因为所有的工作都已经完成了。

Note also that std::async can be launched with policies std::launch::async or std::launch::deferred. If you don't specify it, the implementation is allowed to choose, and it could well choose to use deferred evaluation, which would result in all the work being done when you attempt to get the result from the future, resulting in a longer block. So if you want to make sure that the work is done asynchronously, use std::launch::async.

还要注意,可以使用策略std::launch:::async或std::launch::deferred来启动std:::async。如果不指定它,则允许实现进行选择,并且它可以很好地选择使用延迟求值,这将导致当您试图从未来获得结果时所做的所有工作,从而导致更长的块。因此,如果您想确保工作是异步完成的,请使用std::launch:::async。

#2


62  

  • it's called async, but it got a really "sequential behaviour",
  • 它叫做异步,但它有一个真正的“顺序行为”,

No, if you use the std::launch::async policy then it runs asynchronously in a new thread. If you don't specify a policy it might run in a new thread.

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

basically in the row where you call the future associated with your async function foo, the program blocks until the execution of foo it's completed.

在调用与异步函数foo关联的future的行中,程序会阻塞,直到foo的执行完成。

It only blocks if foo hasn't completed, but if it was run asynchronously (e.g. because you use the std::launch::async policy) it might have completed before you need it.

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

  • it depends on the exact same external library as others, and better, non-blocking solutions, which means pthread, if you want to use std::async you need pthread.
  • 它依赖于与其他库完全相同的外部库,以及更好的非阻塞解决方案(即pthread),如果您希望使用std:::async,则需要pthread。

Wrong, it doesn't have to be implemented using Pthreads (and on Windows it isn't, it uses the ConcRT features.)

错误的是,它不需要使用Pthreads实现(在Windows上不需要使用ConcRT特性)。

at this point it's natural for me asking why choosing std::async over even a simple set of functors ?

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

Because it guarantees thread-safety and propagates exceptions across threads. Can you do that with a simple set of functors?

因为它保证线程安全,并在线程间传播异常。你能用一组简单的函数来做吗?

It's a solution that doesn't even scale at all, the more future you call, the less responsive your program will be.

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

Not necessarily. If you don't specify the launch policy then a smart implementation can decide whether to start a new thread, or return a deferred function, or return something that decides later, when more resources may be available.

不一定。如果不指定启动策略,那么智能实现可以决定是启动一个新线程,还是返回一个递延函数,还是返回一个稍后决定的东西,此时可能有更多的资源可用。

Now, it's true that with GCC's implementation, if you don't provide a launch policy then with current releases it will never run in a new thread (there's a bugzilla report for that) but that's a property of that implementation, not of std::async in general. You should not confuse the specification in the standard with a particular implementation. Reading the implementation of one standard library is a poor way to learn about C++11.

现在,确实有了GCC的实现,如果不提供启动策略,那么在当前版本中,它将永远不会在一个新的线程中运行(为此有一个bugzilla报告),但这是实现的一个属性,而不是一般的std::async。您不应该将标准中的规范与特定的实现混淆。阅读一个标准库的实现不是学习c++ 11的好办法。

Can you show an example that is granted to be executed in an async, non blocking, way ?

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

This shouldn't block:

这个不应该阻止:

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

By specifying the launch policy you force asynchronous execution, and if you do other work while it's executing then the result will be ready when you need it.

通过指定启动策略,可以强制异步执行,如果在执行时执行其他工作,那么当需要时,结果就会就绪。

#3


10  

I think your problem is with std::future saying that it blocks on get. It only blocks if the result isn't already ready.

我认为你的问题是std::future说它会阻碍get。只有在结果尚未准备好时,它才会阻塞。

If you can arrange for the result to be already ready, this isn't a problem.

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

There are many ways to know that the result is already ready. You can poll the future and ask it (relatively simple), you could use locks or atomic data to relay the fact that it is ready, you could build up a framework to deliver "finished" future items into a queue that consumers can interact with, you could use signals of some kind (which is just blocking on multiple things at once, or polling).

有很多方法可以知道结果已经准备好了。你可以调查未来,问它(相对简单),您可以使用锁或原子数据传递的事实,它是准备好了,你可以建立一个框架来实现“完成”未来项目到一个队列,消费者可以相互作用,你可以用某种信号(也就是阻止在多个东西,或轮询)。

Or, you could finish all the work you can do locally, and then block on the remote work.

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

As an example, imagine a parallel recursive merge sort. It splits the array into two chunks, then does an async sort on one chunk while sorting the other chunk. Once it is done sorting its half, the originating thread cannot progress until the second task is finished. So it does a .get() and blocks. Once both halves have been sorted, it can then do a merge (in theory, the merge can be done at least partially in parallel as well).

例如,想象一个并行递归合并排序。它将数组分割成两个块,然后在对另一个块进行排序时对一个块进行异步排序。一旦对它的一半进行排序,原始线程直到第二个任务完成后才能继续。它做了。get()和block。一旦两个部分都被排序,它就可以进行合并(理论上,合并至少可以部分地并行完成)。

This task behaves like a linear task to those interacting with it on the outside -- when it is done, the array is sorted.

这个任务对于与它交互的外部人员来说就像一个线性任务——当它完成时,数组就被排序了。

We can then wrap this in a std::async task, and have a future sorted array. If we want, we could add in a signally procedure to let us know that the future is finished, but that only makes sense if we have a thread waiting on the signals.

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

#4


3  

In the reference: http://en.cppreference.com/w/cpp/thread/async

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

If the async flag is set (i.e. policy & std::launch::async != 0), then async executes the function f on a separate thread of execution as if spawned by std::thread(f, args...), except that if the function f returns a value or throws an exception, it is stored in the shared state accessible through the std::future that async returns to the caller.

如果异步标志设置(即政策& std::发射::异步! = 0),然后一个单独的线程上异步执行函数f的执行如果催生了std::线程(f,args…),除了,如果函数返回一个值或者抛出一个异常,这是存储在共享状态可以通过std::未来异步返回给调用者。

It is a nice property to keep a record of exceptions thrown.

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

#1


46  

If you need the result of an asynchronous operation, then you have to block, no matter what library you use. The idea is that you get to choose when to block, and, hopefully when you do that, you block for a negligible time because all the work has already been done.

如果您需要异步操作的结果,那么无论您使用什么库,都必须阻塞。它的意思是你可以选择什么时候阻塞,希望当你这样做的时候,你可以阻塞一段可以忽略的时间因为所有的工作都已经完成了。

Note also that std::async can be launched with policies std::launch::async or std::launch::deferred. If you don't specify it, the implementation is allowed to choose, and it could well choose to use deferred evaluation, which would result in all the work being done when you attempt to get the result from the future, resulting in a longer block. So if you want to make sure that the work is done asynchronously, use std::launch::async.

还要注意,可以使用策略std::launch:::async或std::launch::deferred来启动std:::async。如果不指定它,则允许实现进行选择,并且它可以很好地选择使用延迟求值,这将导致当您试图从未来获得结果时所做的所有工作,从而导致更长的块。因此,如果您想确保工作是异步完成的,请使用std::launch:::async。

#2


62  

  • it's called async, but it got a really "sequential behaviour",
  • 它叫做异步,但它有一个真正的“顺序行为”,

No, if you use the std::launch::async policy then it runs asynchronously in a new thread. If you don't specify a policy it might run in a new thread.

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

basically in the row where you call the future associated with your async function foo, the program blocks until the execution of foo it's completed.

在调用与异步函数foo关联的future的行中,程序会阻塞,直到foo的执行完成。

It only blocks if foo hasn't completed, but if it was run asynchronously (e.g. because you use the std::launch::async policy) it might have completed before you need it.

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

  • it depends on the exact same external library as others, and better, non-blocking solutions, which means pthread, if you want to use std::async you need pthread.
  • 它依赖于与其他库完全相同的外部库,以及更好的非阻塞解决方案(即pthread),如果您希望使用std:::async,则需要pthread。

Wrong, it doesn't have to be implemented using Pthreads (and on Windows it isn't, it uses the ConcRT features.)

错误的是,它不需要使用Pthreads实现(在Windows上不需要使用ConcRT特性)。

at this point it's natural for me asking why choosing std::async over even a simple set of functors ?

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

Because it guarantees thread-safety and propagates exceptions across threads. Can you do that with a simple set of functors?

因为它保证线程安全,并在线程间传播异常。你能用一组简单的函数来做吗?

It's a solution that doesn't even scale at all, the more future you call, the less responsive your program will be.

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

Not necessarily. If you don't specify the launch policy then a smart implementation can decide whether to start a new thread, or return a deferred function, or return something that decides later, when more resources may be available.

不一定。如果不指定启动策略,那么智能实现可以决定是启动一个新线程,还是返回一个递延函数,还是返回一个稍后决定的东西,此时可能有更多的资源可用。

Now, it's true that with GCC's implementation, if you don't provide a launch policy then with current releases it will never run in a new thread (there's a bugzilla report for that) but that's a property of that implementation, not of std::async in general. You should not confuse the specification in the standard with a particular implementation. Reading the implementation of one standard library is a poor way to learn about C++11.

现在,确实有了GCC的实现,如果不提供启动策略,那么在当前版本中,它将永远不会在一个新的线程中运行(为此有一个bugzilla报告),但这是实现的一个属性,而不是一般的std::async。您不应该将标准中的规范与特定的实现混淆。阅读一个标准库的实现不是学习c++ 11的好办法。

Can you show an example that is granted to be executed in an async, non blocking, way ?

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

This shouldn't block:

这个不应该阻止:

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

By specifying the launch policy you force asynchronous execution, and if you do other work while it's executing then the result will be ready when you need it.

通过指定启动策略,可以强制异步执行,如果在执行时执行其他工作,那么当需要时,结果就会就绪。

#3


10  

I think your problem is with std::future saying that it blocks on get. It only blocks if the result isn't already ready.

我认为你的问题是std::future说它会阻碍get。只有在结果尚未准备好时,它才会阻塞。

If you can arrange for the result to be already ready, this isn't a problem.

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

There are many ways to know that the result is already ready. You can poll the future and ask it (relatively simple), you could use locks or atomic data to relay the fact that it is ready, you could build up a framework to deliver "finished" future items into a queue that consumers can interact with, you could use signals of some kind (which is just blocking on multiple things at once, or polling).

有很多方法可以知道结果已经准备好了。你可以调查未来,问它(相对简单),您可以使用锁或原子数据传递的事实,它是准备好了,你可以建立一个框架来实现“完成”未来项目到一个队列,消费者可以相互作用,你可以用某种信号(也就是阻止在多个东西,或轮询)。

Or, you could finish all the work you can do locally, and then block on the remote work.

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

As an example, imagine a parallel recursive merge sort. It splits the array into two chunks, then does an async sort on one chunk while sorting the other chunk. Once it is done sorting its half, the originating thread cannot progress until the second task is finished. So it does a .get() and blocks. Once both halves have been sorted, it can then do a merge (in theory, the merge can be done at least partially in parallel as well).

例如,想象一个并行递归合并排序。它将数组分割成两个块,然后在对另一个块进行排序时对一个块进行异步排序。一旦对它的一半进行排序,原始线程直到第二个任务完成后才能继续。它做了。get()和block。一旦两个部分都被排序,它就可以进行合并(理论上,合并至少可以部分地并行完成)。

This task behaves like a linear task to those interacting with it on the outside -- when it is done, the array is sorted.

这个任务对于与它交互的外部人员来说就像一个线性任务——当它完成时,数组就被排序了。

We can then wrap this in a std::async task, and have a future sorted array. If we want, we could add in a signally procedure to let us know that the future is finished, but that only makes sense if we have a thread waiting on the signals.

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

#4


3  

In the reference: http://en.cppreference.com/w/cpp/thread/async

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

If the async flag is set (i.e. policy & std::launch::async != 0), then async executes the function f on a separate thread of execution as if spawned by std::thread(f, args...), except that if the function f returns a value or throws an exception, it is stored in the shared state accessible through the std::future that async returns to the caller.

如果异步标志设置(即政策& std::发射::异步! = 0),然后一个单独的线程上异步执行函数f的执行如果催生了std::线程(f,args…),除了,如果函数返回一个值或者抛出一个异常,这是存储在共享状态可以通过std::未来异步返回给调用者。

It is a nice property to keep a record of exceptions thrown.

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