During a code review, I came across a piece of code that basically boils down to this:
#include <iostream>
#include <future>
#include <thread>
int main( int, char ** )
{
std::atomic<int> x( 0 );
std::future<void> task;
for( std::size_t i = 0u; i < 5u; ++i )
{
task = std::async( std::launch::async, [&x, i](){
std::this_thread::sleep_for( std::chrono::seconds( 2u * ( 5u - i ) ) );
++x;
} );
}
task.get();
std::cout << x << std::endl;
return 0;
}
I was not quite sure whether
- it is guaranteed that all tasks are executed when printing out the result,
- whether the tasks would be executed one after another (i.e. the task assignment would be blocking) or not.
I could not answer that question from reading documentation on the internet, so I thought I would write the snippet above to find out what our compiler actually does.
Now, I found out that the answer of what gcc-5 does is indecisive and that made me even more curious: One would assume that the assignment is either blocking or non-blocking.
If it is blocking, the time taken by the program should basically be the sum of the time the individual tasks take to execute. The first one takes 10 seconds, the second 8, the third 6, the fourth 4 and the last 2 seconds. So in total it should take 10+8+6+4+2 = 30 seconds.
If it is non-blocking, it should take as long as the last task, i.e. 2 seconds.
Here is what happens: It takes 18 seconds (measured using time ./a.out or a good old clock). By playing around a bit with the code I found out that the code behaves as if the assignment would be alternatingly blocking and non-blocking.
But this can't be true, right? std::async
probably falls back to std::deferred
half of the time? My debugger says that it spawns two threads, blocks until both threads exit, then spawns two more threads and so on.
What does the standard say? What should happen? What happens inside gcc-5?
Aucun commentaire:
Enregistrer un commentaire