Introduction
Currently, C++11 has introduced async
and future
/promise
pattern. It allows performing concurrent operations and waiting for the result. It looks very promising (in future). Yep.
I don't want to discuss the current standard implementation. It looks like an initial step and contains several flaws (destructor behavior, no thread pools, only blocking semantic for value retrieval etc). I would like to discuss particular usage and overhead questions.
There are several typical usage:
- Start the task asynchronously and wait for the result.
- Start the task asynchronously and don't wait for the result.
- Start several (or a lot) tasks asynchronously and wait for the results.
Let's discuss them in details.
Start the task asynchronously and wait for the result
The idea is simple: sometimes I need start task asynchronously while continuing doing processing at the same time. If the result of the task is needed I invoke the method get()
from the future to obtain the result. It looks pretty simple. The only thing is that this is the only case where future
/promise
technique is very well suited.
Let's consider another typical usage.
Start the task asynchronously and don't wait for the result
The idea is even simpler: I don't want for the result. I just want to start some action to perform operation. Nothing more. Is it possible to implement this case using standard library? No!
But I would like to concentrate on another aspect. Let's suppose that we have detach()
method. OK. What do we have? We have mutex, critical section and other interesting stuff to correctly operate with shared state. But we don't want to have the shared state! Why should I pay for it? Okay.
Let's continue.
Start several (or a lot) tasks asynchronously and wait for the results
So what should I do? I need to create a vector of futures, put all futures inside vector, iterate through them and invoke get()
or wait()
methods. If I'm lucky there is no context switches take place. Really? No!
At each get()
invocation we either have a result (so only mutex.lock()
/unlock()
is called or atomic flag depending on the implementation) or we should wait on condition variable. So in worst case each iteration requires context switch. And this worst case is the common case! Because usually the amount of task work is much more then work needed to iterate through futures (obviously).
And what should I do?
There is a solution! Just use appropriate implementations for different cases. See my implementation as an example:
Below one may find some explanations how to use it:
- If you want to invoke the task asynchronously and wait for result, use
goWait
orWaiter
. - If you want to invoke the task asynchronously and don't wait for result, use
go
. - If you want to invoke several tasks asynchronously and wait for results, use
goWait
orWaiter
. So the library implements several tasks in the same way as described in item #1 - If you want to invoke several tasks asynchronously and wait for the first actual result, use
goAnyWait
orgoAnyResult
.
Try it!
No comments :
Post a Comment