rityzmon rityzmon - 1 month ago 6
Linux Question

Difference in C++11 async behaviour on Mac and Linux

Consider the following C+11 code:

#include <chrono>
#include <future>
#include <iostream>
#include <thread>

using namespace std;

int main() {
auto start = chrono::steady_clock::now();
auto elapsed = [start](string s) {
cout << s << (chrono::steady_clock::now() - start).count() << endl;
};

elapsed("A ");
auto fut (async([]() { this_thread::sleep_for(chrono::seconds(2)); }));
elapsed("B ");
this_thread::sleep_for(chrono::seconds(1));
elapsed("C ");
fut.wait();
elapsed("D ");
return 0;
}


Mac Results



Compiling said code on macOS Sierra with the command
c++ -std=c++11 -stdlib=libc++ -Wall question.cc -o question
and running, I get the output:

A 27186
B 86970
C 1001961755
D 2001585903


which is expected. It took minimal time to get to
A
and
B
, waited 1 second and then got to
C
, and waiting the remaining of the 2 seconds (1 already elapsed) to get to
D
.

The Mac compiler is:

$ c++ --version
Apple LLVM version 8.0.0 (clang-800.0.38)
Target: x86_64-apple-darwin16.0.0
Thread model: posix


Linux Results



On Linux, I compiled the same code with
c++ -std=c++11 -pthread question.cc -o question
and ran, and got the result:

A 32423
B 444340
C 1003635793
D 3006121895


The Linux compiler is:

$ c++ --version
c++ (Debian 4.9.2-10) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.


I also tried
clang++
on Linux, the LLVM C++ compiler. Same result.

Q



Why is there the fully 2 second lag between
C
and
D
on Linux? Wasn't the async task supposed to run in the background? Am I using the wrong compile options?

Answer

async takes as an optional parameter if you want to start it in a different thread, or just wait until the .wait is called to run the code.

You chose "I do not care" by omitting it.

If you care, state you want it in a different thread. If not, you should not be surprised if they waited until you .waited to run it, or alternatively started a new thread and ran it there. In your case, one compiler made it lazy, the other put it in its own thread. Both are permitted by the standard.

To get the behavior you expect, pass std::launch::async as the first argument to std::async.


Now, in theory async without being told how to run it is supposed to do something smart. But some compilers punted and said "always doing it lazy is less work for us" (or always async), so did always-lazy whenever you let it.

This is a quality of implementation issue. At this point, you cannot trust async to be smart on all C++11 compilers you are working with. Come back in a decade.