Quantcast
Channel: Bartek's coding blog
Viewing all articles
Browse latest Browse all 325

Tasks with std::future and std::async

$
0
0
tasks and arrows
Let us consider a simple thing: use some worker thread to compute a value. In the source code it can look like that:
std::thread t([]() { auto res = perform_long_computation(); };
We would like to obtain the result when it is completed of course. But how to do it efficiently?
A shared variable can be used:
MyResult sharedRes;
std::thread t([]() { sharedRes = perform_long_computation(); };
But that way we need to know that thread t is finished and sharedRes contain computed value; A lot of constructions can be used here: mutexes, condition variables, etc... but maybe there is a better and simpler way of doing that?
Let us have a look at this code:
auto result = std::async([]() { return perform_long_computation(); });
MyResult finalResult = result.get();
Is not that simpler? But what actually happened there?

The future

In C++11 in the Standard Library we have all sorts of concurrency features. As usually we have threads, mutexes, atomics, etc. Fortunately for us the library went further and added some more higher level structures. In our example we need to look at future and async.
If we do not want to get into much details all we need to know is that std::future<T> holds a shared state and std::async allow us to run the code asynchronously. In our case we can rewrite it to:
std::future<MyResult> result = std::async([]() { return perform_long_computation(); });
MyResult finalResult = result.get();
Thus result is not a direct value computed in the thread but it is some form of a guard that makes sure the value is ready when we call .get() method. All the magic (synchronization) happens underneath.
This opens some interesting possibilities and we can start with Task Based Parallelism. We can now build some form of a pipeline where data flows from one side to the other, but in the middle computation can be distributed among several threads. In the next post we will look at this topic.

Notes

  • .get() can be called only once! The second time we will get exception. If you want to fetch the result from several threads or several times in single thread you can use std::shared_future.
  • std::async can run code in the same thread as the caller. Launch Policy can be used to force truely asynchrounus call - std::launch::async or std::launch::deferred
  • when there is an exception in the future (inside our lambda or function) this exception will be propagated and rethrown in the .get() method.

References

I will try to write a bit more about this topic in the following posts.

Viewing all articles
Browse latest Browse all 325

Trending Articles