Skip to content
Theory · Multithreading: An Overview · lesson 9 of 12

async and future

You order soup in the kitchen and get a ticket with a number. While the soup cooks, you get on with your own things. When you need the soup, you bring the ticket to the pickup window. If the soup is ready, you get it right away; if not, you wait at the window. A ticket is exchanged for soup once.

A function started in a std::thread cannot return a result: the thread discards the returned value. To get a result, you have to set up a shared variable and call join(). The std::async function from the <future> header makes this simpler. It starts a function and returns a std::future, an object from which you collect the result later, that very ticket:

C++
#include <future>
#include <iostream>

int sum_to(int n) {
    int sum = 0;
    for (int i = 1; i <= n; ++i) {
        sum += i;
    }
    return sum;
}

int main() {
    std::future<int> result = std::async(std::launch::async, sum_to, 100);
    std::cout << result.get() << '\n';  // 5050
}

The ticket and the result

Let's take main apart:

  1. std::async(std::launch::async, sum_to, 100) starts sum_to and passes it the argument 100.
  2. std::future<int> is the ticket's type; the angle brackets hold the result type, here int.
  3. result.get() waits until the function finishes and returns its result: 5050.

std::future has two ways to wait for the function:

  • get() waits and takes the result; you can call it on one std::future only once, as a ticket is exchanged once;
  • wait() only waits, without taking the result.

This is where the ticket analogy ends: the pickup window turns down a used ticket, while a second get() is undefined behavior. The standard promises neither a refusal nor a result.

Launch policy

The first argument of std::async is the launch policy. It decides when and where the function runs.

std::launch::async

The function runs in a new thread and starts right away. This is a kitchen that starts on the soup as soon as it takes the order.

std::launch::deferred

The function does not start right away: the first call to get() or wait() runs it in the calling thread. This is a kitchen that starts cooking only when you come to the window with the ticket. If neither get() nor wait() is called, the function never runs at all.

No policy

You can leave the policy out: std::async(sum_to, 100). Then the standard library implementation chooses between async and deferred. If the task must run in parallel, pass std::launch::async explicitly. Interviewers like to ask whether the function runs at all without an explicit policy.

Key points

  • std::async starts a function and returns a std::future, from which you collect the result later.
  • get() waits and returns the result, and you can call it only once; wait() only waits.
  • With std::launch::async the function runs in a new thread, with std::launch::deferred at the first get() or wait().
  • Without an explicit policy the library chooses; for parallel work, pass std::launch::async.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way