std::async
Определён в заголовочном файле <future> | ||
|---|---|---|
template< class F, class... Args > std::future</* see below */> async( F&& f, Args&&... args ); | (1) | (начиная с C++11) |
template< class F, class... Args >
std::future</* see below */> async( std::launch policy,
F&& f, Args&&... args );
| (2) | (начиная с C++11) |
Шаблон функции std::async асинхронно выполняет функцию f (потенциально в отдельном потоке, который может быть частью пула потоков) и возвращает std::future, в котором впоследствии будет храниться результат вызова этой функции.
policy, равным std::launch::async | std::launch::deferred.Возвращаемый тип std::async — std::future<V>, где V — это:
| typenamestd::result_of<typenamestd::decay<F>::type( | (до C++17) |
| std::invoke_result_t<std::decay_t<F>,std::decay_t<Args>...>. | (начиная с C++17) |
| Если выполнено любое из следующих условий, программа некорректна:
| (до C++20) |
| Если любое из следующих условий
| (начиная с C++20) |
Вызов std::async синхронизируется с вызовом f, а завершение f упорядочено перед переводом общего состояния в готовое состояние.
Параметры
| f | - | Вызываемый объект для вызова |
| args | - | параметры, передаваемые в f |
| policy | - | значение битовой маски, отдельные биты которой задают допустимые способы выполнения |
Возвращаемое значение
std::future, ссылающийся на общее состояние, созданное этим вызовом std::async.
Политики запуска
Асинхронный вызов
Если установлен флаг async, то есть (policy & std::launch::async) != 0, тогда std::async вызывает
|
| (до C++23) |
|
| (начиная с C++23) |
так, как если бы вызов выполнялся в новом потоке исполнения, представленном объектом std::thread.
| Вызовы | (до C++23) |
| Значения, полученные с помощью auto, материализуются в текущем потоке. | (начиная с C++23) |
Если функция f возвращает значение или выбрасывает исключение, оно сохраняется в общем состоянии, доступном через std::future, который std::async возвращает вызывающей стороне.
Отложенный вызов
Если установлен флаг deferred (то есть (policy & std::launch::deferred) != 0), тогда std::async сохраняет
|
| (до C++23) |
|
| (начиная с C++23) |
Выполняется ленивое вычисление:
- При первом вызове функции ожидания без ограничения по времени для
std::future, которыйstd::asyncвозвратил вызывающей стороне, вычисляетсяINVOKE(std::move(g),std::move(xyz))в потоке, вызвавшем функцию ожидания (это не обязательно поток, изначально вызвавшийstd::async), где
| (до C++23) |
| (начиная с C++23) |
- Результат или исключение помещается в общее состояние, связанное с возвращённым
std::future, и только после этого состояние становится готовым. Все последующие обращения к тому жеstd::futureнемедленно возвращают результат.
Другие политики
Если в policy не установлен ни std::launch::async, ни std::launch::deferred, ни какой-либо флаг политики, определённой реализацией, поведение не определено.
Выбор политики
Если установлено несколько флагов, выбранная политика определяется реализацией. Для политики по умолчанию (когда в policy установлены оба флага — std::launch::async и std::launch::deferred) стандарт рекомендует (но не требует) использовать доступный параллелизм и откладывать выполнение остальных задач.
Если выбрана политика std::launch::async,
- вызов функции ожидания для асинхронного объекта результата, разделяющего общее состояние, созданное этим вызовом
std::async, блокируется до завершения связанного потока, как если бы к нему присоединились, либо до истечения времени ожидания; и - завершение связанного потока синхронизируется с успешным возвратом первой функции, ожидающей общее состояние, или с возвратом последней функции, освобождающей общее состояние, в зависимости от того, что произойдёт раньше.
Исключения
Выбрасывает
-
std::bad_alloc, если не удаётся выделить память для внутренних структур данных, или -
std::system_errorс условием ошибкиstd::errc::resource_unavailable_try_again, еслиpolicy == std::launch::asyncи реализация не может запустить новый поток.- Если
policyравенstd::launch::async | std::launch::deferredили содержит дополнительные установленные биты, в этом случае будет выполнен отложенный вызов либо будет использована политика, определённая реализацией.
- Если
Примечания
Реализация может расширить поведение первой перегрузки std::async, включив дополнительные биты политики запуска (определяемые реализацией) в политику запуска по умолчанию.
Примерами политик запуска, определяемых реализацией, являются политика sync (немедленное выполнение в рамках вызова std::async) и политика task (аналогична std::async, но локальные переменные потока не очищаются).
Если std::future, полученный из std::async, не перемещён и не привязан к ссылке, деструктор std::future будет блокировать выполнение в конце полного выражения до завершения асинхронной операции, фактически делая синхронным код, подобный следующему:
std::async(std::launch::async, []{ f(); }); // temporary's dtor waits for f()
std::async(std::launch::async, []{ g(); }); // does not start until f() completes
Обратите внимание, что деструкторы std::future, полученных не посредством вызова std::async, никогда не блокируют выполнение.
Пример
#include <algorithm>
#include <future>
#include <iostream>
#include <mutex>
#include <numeric>
#include <string>
#include <vector>
std::mutex m;
struct X
{
void foo(int i, const std::string& str)
{
std::lock_guard<std::mutex> lk(m);
std::cout << str << ' ' << i << '\n';
}
void bar(const std::string& str)
{
std::lock_guard<std::mutex> lk(m);
std::cout << str << '\n';
}
int operator()(int i)
{
std::lock_guard<std::mutex> lk(m);
std::cout << i << '\n';
return i + 10;
}
};
template<typename RandomIt>
int parallel_sum(RandomIt beg, RandomIt end)
{
auto len = end - beg;
if (len < 1000)
return std::accumulate(beg, end, 0);
RandomIt mid = beg + len / 2;
auto handle = std::async(std::launch::async,
parallel_sum<RandomIt>, mid, end);
int sum = parallel_sum(beg, mid);
return sum + handle.get();
}
int main()
{
std::vector<int> v(10000, 1);
std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n';
X x;
// Calls (&x)->foo(42, "Hello") with default policy:
// may print "Hello 42" concurrently or defer execution
auto a1 = std::async(&X::foo, &x, 42, "Hello");
// Calls x.bar("world!") with deferred policy
// prints "world!" when a2.get() or a2.wait() is called
auto a2 = std::async(std::launch::deferred, &X::bar, x, "world!");
// Calls X()(43); with async policy
// prints "43" concurrently
auto a3 = std::async(std::launch::async, X(), 43);
a2.wait(); // prints "world!"
std::cout << a3.get() << '\n'; // prints "53"
} // if a1 is not done at this point, destructor of a1 prints "Hello 42" here
Возможный вывод:
The sum is 10000 43 world! 53 Hello 42
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были ретроактивно применены к ранее опубликованным стандартам C++.
| DR | Применено к | Поведение согласно опубликованному стандарту | Корректное поведение |
|---|---|---|---|
| LWG 2021 | C++11 | возвращаемый тип был некорректным, а категория значения аргументов в отложенном случае — неясной | возвращаемый тип исправлен, уточнено, что используются rvalue |
| LWG 2078 | C++11 | было неясно, может ли быть выброшено std::system_error,если policy задаёт другиеполитики запуска помимо std::launch::async | может быть выброшено только еслиpolicy == std::launch::async |
| LWG 2100 | C++11 | функции ожидания с ограничением по времени не могли завершиться по тайм-ауту при использовании политики std::launch::async | разрешено |
| LWG 2120 | C++11 | поведение было неясным, если не задана стандартная политика или политика, определённая реализацией | в этом случае поведение не определено |
| LWG 2186 | C++11 | было неясно, как обрабатываются возвращённое значение и исключение, выброшенное при ленивом вычислении | они сохраняются в общем состоянии |
| LWG 2752 | C++11 |
std::async могла не выбрасывать std::bad_alloc, еслине удавалось выделить память для внутренних структур данных | выбрасывает |
| LWG 3476 | C++20 | (декейированные типы) F и типы аргументовдолжны были напрямую поддерживать конструирование перемещением | эти требования удалены[1] |
- Поддержка конструирования перемещением уже косвенно требуется
std::is_constructible_v.
См. также
|
(C++11) | ожидает асинхронно установленного значения (шаблон класса) |
| Документация C++ для библиотеки поддержки выполнения | |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/thread/async