async & future
std::thread directly means manually managing joins, and there's no built-in way to get a return value back from the thread function. std::async and std::future, both from <future>, solve exactly that: launch a task that computes a value, and collect the result later without touching threads directly.std::async — launch a task
std::async takes a callable (and its arguments) and returns a std::future representing the eventual result. Depending on the launch policy, the task may run on a new thread immediately, or be deferred until you actually ask for the result.basic-async.cpp
#include <future>
#include <iostream>
int computeSquare(int n) {
return n * n;
}
int main() {
std::future<int> result = std::async(computeSquare, 12);
std::cout << "Doing other work while it computes...\n";
int value = result.get(); // blocks until the result is ready
std::cout << "Result: " << value << "\n";
return 0;
}future::get() — retrieving the result
.get() waits until the asynchronous task finishes, then returns its value (or rethrows an exception if the task threw one). Once you call .get(), the future is consumed — calling it a second time on the same future is undefined behavior. If you need to check multiple times, use .wait() or .wait_for() to check status without consuming the result.Launch policies
std::async controls when and how the task actually runs.Policy | Behavior |
|---|---|
| Forces the task to start running immediately on a new thread. |
| Runs lazily — nothing happens until |
(default, no policy given) | Implementation-defined mix of both — the runtime picks whichever it prefers, which may not be a new thread at all. |
launch-policies.cpp
#include <future>
int slowTask() {
// imagine expensive work here
return 42;
}
int main() {
auto f1 = std::async(std::launch::async, slowTask); // runs now, on a new thread
auto f2 = std::async(std::launch::deferred, slowTask); // runs only when .get() is called
int a = f1.get();
int b = f2.get(); // slowTask actually executes right here
return a + b;
}std::launch::async explicitly. Leaving the policy unspecified means the standard library is free to defer the task instead of running it concurrently.Worked example: background work + other work
background-computation.cpp
#include <future>
#include <iostream>
#include <vector>
long long sumRange(long long from, long long to) {
long long total = 0;
for (long long i = from; i <= to; ++i) total += i;
return total;
}
int main() {
// Start summing the first half in the background...
std::future<long long> firstHalf =
std::async(std::launch::async, sumRange, 1, 500000);
// ...while the main thread sums the second half.
long long secondHalf = sumRange(500001, 1000000);
long long total = firstHalf.get() + secondHalf;
std::cout << "Total: " << total << "\n";
return 0;
}std::async handled the thread lifecycle — no manual join() was needed.std::promise — the lower-level primitive
std::async and std::future are built on a more primitive pair: std::promise and its associated future. A std::promise lets you set a value (or exception) from one thread that a corresponding std::future can retrieve from another — useful when you need to hand a result across threads without std::async's automatic task launching, for example when integrating with a custom thread pool.promise-basics.cpp
#include <future>
#include <iostream>
#include <thread>
void produceValue(std::promise<int> prom) {
prom.set_value(99);
}
int main() {
std::promise<int> prom;
std::future<int> fut = prom.get_future();
std::thread t(produceValue, std::move(prom));
std::cout << "Waiting for value: " << fut.get() << "\n";
t.join();
return 0;
}std::asynclaunches a task and returns astd::future<T>for its eventual result..get()blocks until the result is ready and can only be called once per future.std::launch::asyncforces a new thread;std::launch::deferredruns lazily on.get().std::promiseis the lower-level primitive that lets you manually deliver a value to a future from any thread.