std::async / std::future
The <future> header in C++ provides std::async and std::future for writing asynchronous code without directly managing thread creation and joining. std::async launches a function asynchronously and returns a std::future through which you retrieve the result later. Because thread lifetime is managed automatically, this is generally safer than using std::thread directly.
Syntax
#include <future>
#include <iostream>
int compute() { return 42; }
// std::launch::async -> start a new thread immediately
// std::launch::deferred -> execute lazily on the calling thread when get() is called
// Omitting the policy is implementation-defined (explicitly specifying async is common)
std::future<int> fut = std::async(std::launch::async, compute);
// Retrieve the result (blocks until ready)
// get() can be called only once (calling it a second time is undefined behavior)
int result = fut.get();
// wait() blocks until the result is ready without retrieving it
fut.wait();
// wait_for() waits for a specified duration
std::future_status status = fut.wait_for(std::chrono::seconds(1));
if (status == std::future_status::ready) {
// result is available
} else if (status == std::future_status::timeout) {
// timed out
} else {
// deferred (not yet executed)
}
// std::promise: set a value manually
std::promise<int> prom;
std::future<int> fut2 = prom.get_future();
prom.set_value(100); // sets the value returned by fut2.get()
// prom.set_exception(...) // can also propagate exceptions
Syntax Reference
| Syntax / Class | Description |
|---|---|
| std::async(policy, f, args...) | Launches function f asynchronously and returns a std::future. Specifying std::launch::async starts a new thread immediately. |
| std::future<T> | An object representing the future result of an asynchronous operation. Calling get() blocks until the result is ready. |
| future.get() | Retrieves the result of the asynchronous operation. Blocks if not yet complete. Can be called only once. |
| future.wait() | Blocks until the result is ready without retrieving it. |
| future.wait_for(duration) | Waits for a given duration and returns a std::future_status. Useful for timeout detection. |
| future.valid() | Returns whether the future has a valid shared state. Becomes false after get() is called. |
| std::promise<T> | An object for manually setting a value or exception on a future. Use get_future() to obtain the associated future. |
| promise.set_value(v) | Sets the value that the associated future will return. |
| promise.set_exception(e) | Propagates an exception to the associated future. The exception is rethrown when get() is called. |
| std::shared_future<T> | A future that can be get()-ed multiple times and shared between threads. Obtained via future::share(). |
Sample Code
eva_async.cpp
#include <iostream>
#include <future>
#include <chrono>
#include <string>
#include <stdexcept>
// Ikari Shinji: analyzes the A.T. Field strength of an Angel
int analyzeShinji(const std::string& angelName) {
std::cout << "[Ikari Shinji] Starting A.T. Field analysis for "
<< angelName << "..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(300));
std::cout << "[Ikari Shinji] Analysis complete." << std::endl;
return 8750;
}
// Ayanami Rei: determines the core position of an Angel
std::string analyzeRei(const std::string& angelName) {
std::cout << "[Ayanami Rei] Starting core position identification for "
<< angelName << "." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
std::cout << "[Ayanami Rei] Identification complete." << std::endl;
return "Central chest, depth 12.5m";
}
// Asuka Langley: calculates Unit-02 maximum output
// Simulates failure with an exception
double calcAsuka(bool syncRateOk) {
std::cout << "[Asuka Langley] Starting max output calculation!" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(150));
if (!syncRateOk) {
throw std::runtime_error("Sync rate too low!");
}
std::cout << "[Asuka Langley] Calculation complete. Of course, I am a genius." << std::endl;
return 14.5;
}
// Nagisa Kaworu: passes an Angel identification code via std::promise
void kaworu(std::promise<std::string>& prom) {
std::cout << "[Nagisa Kaworu] Generating Angel identification code." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(250));
prom.set_value("TABRIS-17-OMEGA");
std::cout << "[Nagisa Kaworu] Generation complete." << std::endl;
}
// Katsuragi Misato coordinates all analyses
int main() {
std::cout << "=== NERV Angel Interception Data Integration System ===" << std::endl;
std::cout << "[Katsuragi Misato] Requesting analyses from all pilots." << std::endl << std::endl;
const std::string targetAngel = "Angel 17 Tabris";
// Launch each pilot's task asynchronously
std::future<int> futShinji =
std::async(std::launch::async, analyzeShinji, targetAngel);
std::future<std::string> futRei =
std::async(std::launch::async, analyzeRei, targetAngel);
std::future<double> futAsuka =
std::async(std::launch::async, calcAsuka, true);
// Kaworu sets the value via std::promise
std::promise<std::string> kaworuPromise;
std::future<std::string> futKaworu = kaworuPromise.get_future();
std::future<void> kaworuThread =
std::async(std::launch::async, kaworu, std::ref(kaworuPromise));
std::cout << std::endl;
std::cout << "[Katsuragi Misato] Waiting for all analyses..." << std::endl << std::endl;
// wait_for with timeout
std::future_status shinji_status = futShinji.wait_for(std::chrono::seconds(2));
if (shinji_status == std::future_status::ready) {
int atFieldStrength = futShinji.get();
std::cout << "[Katsuragi Misato] Shinji's result: A.T. Field strength = "
<< atFieldStrength << " cross-sections" << std::endl;
} else {
std::cout << "[Katsuragi Misato] Shinji's analysis timed out." << std::endl;
}
// Retrieve Rei's result (blocks if not yet complete)
std::string corePosition = futRei.get();
std::cout << "[Katsuragi Misato] Rei's result: Core position = " << corePosition << std::endl;
// Retrieve Asuka's result with exception handling
try {
double maxOutput = futAsuka.get();
std::cout << "[Katsuragi Misato] Asuka's result: Unit-02 max output = "
<< maxOutput << " TW" << std::endl;
} catch (const std::exception& e) {
std::cout << "[Katsuragi Misato] Exception from Asuka's task: " << e.what() << std::endl;
}
// Retrieve Kaworu's promise-based result
std::string angelId = futKaworu.get();
kaworuThread.get();
std::cout << "[Katsuragi Misato] Kaworu's identification code: " << angelId << std::endl;
std::cout << std::endl;
std::cout << "=== All analyses complete. Begin interception. Go! ===" << std::endl;
return 0;
}
g++ -std=c++11 eva_async.cpp -o eva_async -lpthread ./eva_async === NERV Angel Interception Data Integration System === [Katsuragi Misato] Requesting analyses from all pilots. [Ikari Shinji] Starting A.T. Field analysis for Angel 17 Tabris... [Ayanami Rei] Starting core position identification for Angel 17 Tabris. [Asuka Langley] Starting max output calculation! [Nagisa Kaworu] Generating Angel identification code. [Asuka Langley] Calculation complete. Of course, I am a genius. [Ayanami Rei] Identification complete. [Nagisa Kaworu] Generation complete. [Katsuragi Misato] Waiting for all analyses... [Ikari Shinji] Analysis complete. [Katsuragi Misato] Shinji's result: A.T. Field strength = 8750 cross-sections [Katsuragi Misato] Rei's result: Core position = Central chest, depth 12.5m [Katsuragi Misato] Asuka's result: Unit-02 max output = 14.5 TW [Katsuragi Misato] Kaworu's identification code: TABRIS-17-OMEGA === All analyses complete. Begin interception. Go! ===
Common Mistake 1: Calling future::get() more than once
std::future::get() can only be called once. Calling it a second time is undefined behavior. Use std::shared_future when multiple calls to get() are needed.
// NG: calling get() twice causes undefined behavior
std::future<int> fut = std::async(std::launch::async, []() { return 42; });
int r1 = fut.get(); // first call: OK
int r2 = fut.get(); // second call: undefined behavior
OK: Convert to std::shared_future for multiple get() calls.
// OK: shared_future allows multiple get() calls
std::shared_future<int> sfut = std::async(std::launch::async, []() { return 42; }).share();
int r1 = sfut.get(); // OK
int r2 = sfut.get(); // OK
Common Mistake 2: Omitting the launch policy
Omitting the launch policy makes the behavior implementation-defined; the task might be deferred rather than launched on a new thread. Specify std::launch::async explicitly when immediate execution on a new thread is required.
// Caution: policy omitted — may be deferred auto fut = std::async(computeHeavyTask); // OK: explicitly request a new thread auto fut = std::async(std::launch::async, computeHeavyTask);
Overview
std::async is a higher-level asynchronous execution mechanism than std::thread. Specifying std::launch::async starts a new thread immediately and returns a std::future. Calling std::future::get() blocks until the operation completes and then retrieves the result; thread join management is automatic. Exceptions thrown inside the asynchronous operation are stored in the future and rethrown when get() is called, making them safe to handle with try-catch. std::promise is useful when you need to pass a value to a future from a callback or external event rather than a function return value. Once get() has been called, a std::future becomes invalid, but converting it to a std::shared_future via share() allows the same result to be accessed from multiple threads. On Linux, link with -lpthread for thread support.
If you find any errors or copyright issues, please contact us.