Cppasync & future

async & future

Working with 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

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() blocks, and can only be called once
Calling .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
The optional first argument to std::async controls when and how the task actually runs.

Policy

Behavior

std::launch::async

Forces the task to start running immediately on a new thread.

std::launch::deferred

Runs lazily — nothing happens until .get() or .wait() is called, at which point it runs synchronously on the calling thread.

(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

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;
}
Note
If you specifically need parallelism, pass 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

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;
}
Both halves of the sum can compute at the same time on separate cores, and std::async handled the thread lifecycle — no manual join() was needed.
std::promise — the lower-level primitive
Under the hood, 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

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::async launches a task and returns a std::future<T> for its eventual result.

  • .get() blocks until the result is ready and can only be called once per future.

  • std::launch::async forces a new thread; std::launch::deferred runs lazily on .get().

  • std::promise is the lower-level primitive that lets you manually deliver a value to a future from any thread.