Futures, Promises, and Asynchronous Tasks
Advanced Concurrency, SIMD, and Performance

9.4 Futures, Promises, and Asynchronous Tasks

A std::thread does not return a value directly, so naïve designs often reach for shared variables, mutexes, and signals. Futures and promises provide one standard mechanism for communicating a result and an exception across an asynchronous boundary.

C++ solves this by providing asynchronous tasks, futures, and promises.

Asynchronous Tasks with `std::async` and Launch Policy

std::async returns a future, but its default policy may run the task asynchronously or defer it until get() or wait() on the calling thread. Request std::launch::async when asynchronous execution is required, while remembering that the standard does not require a particular thread-pool implementation.

A task started with std::launch::async is permitted to use a new thread. Launching an unbounded number of tasks can exhaust resources or degrade throughput, but it is not accurate to promise a crash or assume a reusable pool. Choose a bounded executor or application-managed worker pool when the workload needs explicit scheduling, back-pressure, or cancellation.

#include <iostream>
#include <future>

int calculateSum(int a, int b) {
    return a + b;
}

int main() {
    auto resultFuture = std::async(std::launch::async, calculateSum, 100, 200);
    // Other independent work may run here.
    const int sum = resultFuture.get(); // Waits and rethrows any task exception.
    std::cout << "Result: " << sum << '\n';
    return 0;
}
Request asynchronous execution explicitly when the overlap matters.

Futures and Promises: The Value Channel

If you want finer control, you can separate the transmission channel using std::promise and std::future. A promise is the write end of a channel, and a future is the read end. This allows you to pass a value from a background worker thread back to the main thread:

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

void workerTask(std::promise<int> valPromise) {
    // Perform calculations...
    int result = 42;
    valPromise.set_value(result); // Send the value through the channel
}

int main() {
    std::promise<int> myPromise;
    std::future<int> myFuture = myPromise.get_future();

    std::thread t{workerTask, std::move(myPromise)};
    
    std::cout << "Value from thread: " << myFuture.get() << '\n';
    t.join();
    return 0;
}
Sending values from threads using promises.
  • The Restaurant Buzzer Metaphor: Using futures is like ordering food at a restaurant counter. You pay, and they hand you a small buzzer (a future). You do not stand at the counter waiting (busy waiting). You go sit down, check your phone, and relax. When the food is ready, the kitchen triggers the signal (satisfies the promise), your buzzer flashes, and you retrieve your meal (future.get()).
Finished reading this lesson?