ReviseAlgo Logo

Concurrency

std::async

Launch asynchronous tasks using std::async, understand policies, and avoid the temporary future destructor trap.

Interview: Differences between launch policies, and why omitting the return future forces synchronous execution.

Last Updated: June 13, 2026 9 min read

The std::async function template in <future> is a high-level wrapper used to run asynchronous tasks. It manages thread creation internally and returns a std::future containing the result.

std::launch::async

Forces the task to run immediately on a new background thread.

std::launch::deferred

Postpones task execution until the consumer thread calls get() or wait().

Destructor Trap

If the returned future is not stored in a variable, the call blocks the current thread until the task completes.

The std::async Future Destructor Trap

A common mistake when using std::async is ignoring its return value:

std::async(std::launch::async, task); // BLOCKS!

By design, the destructor of a future returned by std::async blocks until the associated task completes. If you do not assign the return value to a variable, the temporary future is destroyed at the end of the statement, forcing the task to run synchronously.

Code Walkthrough

Demonstrates the differences in behavior between the async and deferred launch policies.

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

int fetchValue() { std::this_thread::sleep_for(std::chrono::milliseconds(100)); return 100; }

int main() { // 1. Force execution on a background thread std::future<int> f1 = std::async(std::launch::async, fetchValue);

// 2. Defer execution until get() is called std::future<int> f2 = std::async(std::launch::deferred, fetchValue);

std::cout << "Main thread executing concurrently...\n";

std::cout << "Result 1: " << f1.get() << std::endl; // Retrieve result std::cout << "Result 2: " << f2.get() << std::endl; // Task runs here

return 0; }

Interview-Relevant Information

Q: What is the default launch policy of std::async?
Answer: The default policy is std::launch::async | std::launch::deferred. This allows the runtime to decide whether to create a new thread or defer execution based on system load, making task scheduling unpredictable. To guarantee thread creation, specify std::launch::async explicitly.

Q: Why does standard std::async block if you do not store the return value?
Answer: The destructor of a future returned by std::async is specified to block until the task finishes. If the returned future is not stored, it is treated as a temporary and destroyed immediately at the semicolon, blocking execution.

Quick Checklist

Did you specify a launch policy? Did you store the returned future? If yes, your async calls will execute as expected.

Use Cases

Running independent, database-intensive read queries in parallel to accelerate dashboard load times.

Offloading file log writes without blocking application execution.

Common Mistakes

Ignoring the return value of std::async, converting the call into a synchronous blocking operation.

Relying on the default launch policy, which can result in tasks being deferred indefinitely if resources are constrained.