Why should I use std::async?

76,150

Solution 1

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.

Solution 2

  • 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.

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.

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.

  • 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.

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

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

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.

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.

Solution 3

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

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).

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.

Solution 4

In the reference: 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.

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

Share:
76,150

Related videos on Youtube

user2485710
Author by

user2485710

Updated on July 05, 2022

Comments

  • user2485710
    user2485710 almost 2 years

    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 :

    • 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.
    • 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.

    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.

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

    • user2485710
      user2485710 over 10 years
      @rsaxvc where you call the async function, for example future.get()
    • DanielKO
      DanielKO over 10 years
      Your assumptions are wrong. async() is designed to provide a synchronization point so you can get the result of the function being evaluated asynchronously.
    • Cory Nelson
      Cory Nelson about 10 years
      C++'s current idea of "async" doesn't really bring anything significant (other than portability) to the table in comparison to other options. Once it gets continuation-on-completion support (which is a vital part of what virtually every other platform calls "async"), I suspect you'll find many more uses for it.
    • Ciro Santilli OurBigBook.com
      Ciro Santilli OurBigBook.com almost 5 years
      This shows how async makes getting return values super simple: stackoverflow.com/questions/7686939/…
  • user2485710
    user2485710 over 10 years
    Why it "needs" to block ? why not assuming that an async function needs to be executed as soon as possible and give the ability to retrieve just the result, and most importantly, if the C++11 is proposing a new threading model why not using it in the definition of std::async ? std::async doesn't even grant that more than 1 thread will be used. Also AFAIK methods under std::launch:: are just wrappers to future , it's the same blocking concept of execution.
  • user2485710
    user2485710 over 10 years
    that's not a guarantee, according to the standard I can't see why a given C++11 implementation is forced to use something something different than an "usual" single threaded execution.
  • juanchopanza
    juanchopanza over 10 years
    @user2485710 it needs to block when you retrieve the result, if you need the result in the launching thread. It cannot use the result if the result is not ready. So if you go to get the result, you have to wait until it is ready. If it is already ready, then the blocking time will be negligible.
  • juanchopanza
    juanchopanza over 10 years
    @user2485710 also note, in my experience, std::async has asynchronous behaviour, as expected.
  • user2485710
    user2485710 over 10 years
    can you provide an example where the async behaviour of this is observable ?
  • R. Martinho Fernandes
    R. Martinho Fernandes over 10 years
    @user2485710 it is forced because the standard says so: "as if in a new thread of execution" (§30.6.8/3).
  • R. Martinho Fernandes
    R. Martinho Fernandes over 10 years
  • JohannesD
    JohannesD over 10 years
    @user2485710: and "as if in a new thread of execution" has observable effects, for instance thread locals. Of course, if the compiler can prove that it's not observable, it's free to execute it synchronously under the as-if rule, but this is a quality-of-implementation issue.
  • chill
    chill over 10 years
    @juanchopanza, there's a difference between "need to block" and "may block" :)
  • juanchopanza
    juanchopanza over 10 years
    @chill It needs to block if you need the result of the future. There is no getting around that.
  • chill
    chill over 10 years
    @juanchopanza, and if the result is already ready, it "needs" to block waiting for what?
  • juanchopanza
    juanchopanza over 10 years
    @chill it blocks to return the result. Just like any synchronous function would. Getting from the future constitutes a synchronization point.
  • chill
    chill over 10 years
    @juanchopanza, ok, not going to turn this into a chat, what you say makes absolutely no sense, "blocking" in the context of process/thread execution has the well accepted semantics of putting the execution out of "running" into some kind of a "waiting" state, neither of which necessarily happens here. Over.
  • GuLearn
    GuLearn over 10 years
    Very nice answer! I have a question about the last example: although you can force the doSomethingThatTakesTenSeconds to be launched in a separate thread, but you cannot force it to run "immediately", right? If that's the case, then the 'fut.get()' could still block.
  • Jonathan Wakely
    Jonathan Wakely over 10 years
    Yes, but if it takes that long for a new thread to start then your system is horribly overloaded or its scheduler is rubbish.
  • doug65536
    doug65536 almost 8 years
    I measured on coliru that there is around 100µs to 300µs of latency. Around 33% of a millisecond max is good. I would be curious to know what that code does on windows VS vs linux g++ on the same box.
  • Franklin Yu
    Franklin Yu almost 7 years
    How does implementation decide the policy? For example, I would like std::launch::deferred when the computation is cheaper than thread scheduling (which may go to kernel)? (Sorry I'm not familiar with std::thread and assume that it's scheduled by OS.)
  • Jonathan Wakely
    Jonathan Wakely about 6 years
    Ask your own question, don't hijack the comments in other questions.
  • No-Bugs Hare
    No-Bugs Hare about 6 years
    You have to wait for sure, but it doesn't mean you have to block. In particular, continuations and co_await are two ways to handle waiting without blocking (and more importantly - without thread context switching which is inevitably caused by blocking).
  • uLoop
    uLoop about 6 years
    "and on Windows it isn't, it uses the ConcRT features" unless one uses GCC on Windows, in which case it may use WinPthreads or similar