Date: Thu, 21 May 2026 13:17:23 +0000
If you look through a codebase and find an invocation of 'std::async' with
a discarded return value, it's almost certainly a bug.
If you write code like the following:
int main(void)
{
std::async( std::launch::async, lambdaA );
std::async( std::launch::async, lambdaB );
std::async( std::launch::async, lambdaC );
}
The first thread will complete its execution before the second one begins.
The third one won't begin its execution until the first two are finished.
If you want concurrency, you need to do:
int main(void)
{
auto const f1 = std::async( std::launch::async, lambdaA );
auto const f2 = std::async( std::launch::async, lambdaB );
auto const f3 = std::async( std::launch::async, lambdaC );
}
We have named f1, f2 and f3 even though we won't use them, so maybe the
compiler will give a warning for an used variable.
Let's simplify the signature of 'std::async' as follows:
future<int> async( int(*)(void) );
So the problem here is that when you invoke it as follows:
std::async( Func );
The return value, i.e. the 'future' object, will live until the end of the
statement. So at the semi-colon, the destructor of the 'future' object is
invoked -- which waits for the 'int' to become set.
But what if we could define the 'async' function in such a way that its
return value will persist until the end of the current scope -- not until
the end of the current statement. So the declaration of 'async' becomes:
__persistent future<int> async( int(*)(void) );
This would mean we would be able to do:
int main(void)
{
std::async( std::launch::async, lambdaA );
std::async( std::launch::async, lambdaB );
std::async( std::launch::async, lambdaC );
}
a discarded return value, it's almost certainly a bug.
If you write code like the following:
int main(void)
{
std::async( std::launch::async, lambdaA );
std::async( std::launch::async, lambdaB );
std::async( std::launch::async, lambdaC );
}
The first thread will complete its execution before the second one begins.
The third one won't begin its execution until the first two are finished.
If you want concurrency, you need to do:
int main(void)
{
auto const f1 = std::async( std::launch::async, lambdaA );
auto const f2 = std::async( std::launch::async, lambdaB );
auto const f3 = std::async( std::launch::async, lambdaC );
}
We have named f1, f2 and f3 even though we won't use them, so maybe the
compiler will give a warning for an used variable.
Let's simplify the signature of 'std::async' as follows:
future<int> async( int(*)(void) );
So the problem here is that when you invoke it as follows:
std::async( Func );
The return value, i.e. the 'future' object, will live until the end of the
statement. So at the semi-colon, the destructor of the 'future' object is
invoked -- which waits for the 'int' to become set.
But what if we could define the 'async' function in such a way that its
return value will persist until the end of the current scope -- not until
the end of the current statement. So the declaration of 'async' becomes:
__persistent future<int> async( int(*)(void) );
This would mean we would be able to do:
int main(void)
{
std::async( std::launch::async, lambdaA );
std::async( std::launch::async, lambdaB );
std::async( std::launch::async, lambdaC );
}
Received on 2026-05-21 13:17:27
