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,
|
(2) | (начиная с C++11) |
Шаблон функции
std::async
запускает функцию
f
асинхронно (потенциально в отдельном потоке, который может быть частью пула потоков) и возвращает
std::future
, который в конечном итоге будет содержать результат этого вызова функции.
Возвращаемый тип
std::async
— это
std::
future
<
V
>
, где
V
представляет:
|
typename
std::
result_of
<
typename
std::
decay
<
F
>
::
type
(
|
(до C++17) |
|
std:: invoke_result_t < std:: decay_t < F > , std:: decay_t < Args > ... > . |
(начиная с C++17) |
|
Если выполняется любое из следующих условий, программа является некорректной:
|
(до C++20) |
|
Если любое из следующих условий является false , программа является некорректной:
|
(начиная с C++20) |
Вызов
std::async
синхронизируется с
вызовом
f
, и завершение
f
упорядочено перед
подготовкой общего состояния.
Содержание |
Параметры
| f | - | Callable объект для вызова |
| args | - | параметры для передачи в f |
| policy | - | битовая маска, где отдельные биты управляют разрешёнными методами выполнения |
Возвращаемое значение
std::future
ссылающийся на общее состояние, созданное этим вызовом
std::async
.
Политики запуска
Асинхронный вызов
Если установлен флаг
async
, т.е.
(
policy
&
std::
launch
::
async
)
!
=
0
, тогда
std::async
вызывает
|
INVOKE
(
decay-copy
(
std::
forward
<
F
>
(
f
)
)
,
|
(до C++23) |
|
std::
invoke
(
auto
(
std::
forward
<
F
>
(
f
)
)
,
|
(начиная с C++23) |
как если бы в новом потоке выполнения, представленном объектом std::thread .
|
Вызовы decay-copy вычисляются в текущем потоке. |
(до C++23) |
|
Значения, создаваемые auto , материализуются в текущем потоке. |
(начиная с C++23) |
Если функция
f
возвращает значение или выбрасывает исключение, оно сохраняется в общем состоянии, доступном через
std::future
, который
std::async
возвращает вызывающей стороне.
Отложенный вызов
Если установлен флаг
deferred
(т.е.
(
policy
&
std::
launch
::
deferred
)
!
=
0
), тогда
std::async
сохраняет
|
decay-copy ( std:: forward < F > ( f ) ) и decay-copy ( std:: forward < Args > ( args ) ) ... в общем состоянии. |
(до C++23) |
|
auto ( std:: forward < F > ( f ) ) и auto ( std:: forward < Args > ( args ) ) ... в общем состоянии. |
(начиная с C++23) |
Ленивое вычисление выполняется:
-
Первый вызов функции неограниченного ожидания для
std::future
, который вернул
std::asyncвызывающей стороне, выполнит вычисление INVOKE ( std :: move ( g ) , std :: move ( xyz ) ) в потоке, вызвавшем функцию ожидания (который не обязательно должен быть потоком, изначально вызвавшимstd::async), где
|
(до C++23) |
|
(начиная с C++23) |
- Результат или исключение помещается в общее состояние, связанное с возвращённым std::future и только после этого оно переводится в состояние готовности. Все последующие обращения к тому же std::future будут немедленно возвращать результат.
Другие политики
Если ни std::launch::async , ни std::launch::deferred , ни какие-либо флаги политики, определяемые реализацией, не установлены в policy , поведение не определено.
Выбор политики
Если установлено более одного флага, выбор политики определяется реализацией. Для значения по умолчанию (когда установлены оба флага std::launch::async и std::launch::deferred в policy ), стандарт рекомендует (но не требует) использовать доступную параллельность и откладывать любые дополнительные задачи.
Если выбрана политика 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
путем включения дополнительных (определяемых реализацией) битов в политику запуска по умолчанию.
Примерами реализационно-определенных политик запуска являются синхронная политика (выполнение немедленно, внутри вызова
std::async
) и политика задач (аналогично
std::async
, но thread-locals не очищаются)
Если
std::future
полученный из
std::async
не перемещается и не привязывается к ссылке, деструктор
std::future
будет блокироваться в конце полного выражения до завершения асинхронной операции, что фактически делает следующий код синхронным:
std::async(std::launch::async, []{ f(); }); // деструктор временного объекта ожидает завершения f() std::async(std::launch::async, []{ g(); }); // не запускается до завершения f()
Обратите внимание, что деструкторы
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 | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| LWG 2021 | C++11 |
return type incorrect and value category
of arguments unclear in the deferred case |
corrected return type and
clarified that rvalues are used |
| LWG 2078 | C++11 |
it was unclear whether
std::system_error
may be thrown if policy specifies other launch policies besides std::launch::async |
can only be thrown if
policy == std:: launch :: async |
| LWG 2100 | C++11 |
timed waiting functions could not timeout
if std::launch::async policy is used |
allowed |
| LWG 2120 | C++11 |
the behavior was unclear if no standard
or implementation-defined policy is set |
the behavior is
undefined in this case |
| LWG 2186 | C++11 |
it was unclear how the value returned and the
exception thrown from the lazy evaluation are handled |
they are stored in
the shared state |
| LWG 2752 | C++11 |
std::async
might not throw
std::bad_alloc
if the
memory for the internal data structures cannot be allocated |
throws |
| LWG 3476 | C++20 |
(the decayed types of)
F
and the argument types
were directly required to be move constructible |
removed these requirements [1] |
- ↑ Возможность конструирования перемещением уже косвенно требуется std::is_constructible_v .
Смотрите также
|
(C++11)
|
ожидает значение, которое устанавливается асинхронно
(шаблон класса) |
|
C++ documentation
для
Execution support library
|
|