Перейти к содержимому
Теория · Многопоточность: обзор · урок 9 из 12

async и future

Вы заказываете на кухне суп и получаете талон с номером. Пока суп варится, вы заняты своим. Когда суп нужен, вы подходите к окну выдачи с талоном: если суп готов, его отдают сразу, если нет — ждёте у окна. Талон обменивают на суп один раз.

Функция, запущенная в std::thread, не может вернуть результат: возвращённое значение поток отбрасывает. Чтобы забрать результат, приходится заводить общую переменную и вызывать join(). Функция std::async из заголовка <future> упрощает это. Она запускает функцию и возвращает std::future — объект, из которого результат забирают позже, тот самый талон:

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
}

Талон и результат

Давайте разберём main по частям:

  1. std::async(std::launch::async, sum_to, 100) запускает sum_to и передаёт ей аргумент 100.
  2. std::future<int> — тип талона; в угловых скобках стоит тип результата, здесь int.
  3. result.get() ждёт, пока функция закончит работу, и возвращает её результат: 5050.

У std::future два способа дождаться функции:

  • get() ждёт и забирает результат; вызвать его у одного std::future можно только один раз, как один раз обменивают талон;
  • wait() только ждёт, не забирая результат.

Здесь аналогия с талоном заканчивается: у окна выдачи использованный талон не примут, а повторный get() — неопределённое поведение. Стандарт не обещает ни отказа, ни результата.

Политика запуска

Первый аргумент std::async — политика запуска. Она решает, когда и где выполнится функция.

std::launch::async

Функция выполняется в новом потоке и начинает работу сразу. Это кухня, которая берётся за суп, как только приняла заказ.

std::launch::deferred

Функция сразу не запускается: её выполнит первый вызов get() или wait(), в том потоке, который его сделал. Это кухня, которая начинает варить, только когда вы подошли к окну с талоном. Если не вызвать ни get(), ни wait(), функция не выполнится вовсе.

Без политики

Политику можно не указывать: std::async(sum_to, 100). Тогда между async и deferred выбирает реализация стандартной библиотеки. Если задача должна идти параллельно, передайте std::launch::async явно. На собеседовании любят спрашивать, выполнится ли функция без явной политики.

Главное

  • std::async запускает функцию и возвращает std::future, из которого результат забирают позже.
  • get() ждёт и возвращает результат, и вызвать его можно только один раз; wait() только ждёт.
  • С std::launch::async функция выполняется в новом потоке, с std::launch::deferred — при первом get() или wait().
  • Без явной политики выбор делает библиотека; для параллельной работы передайте std::launch::async.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом