Namespaces
Variants

std:: async

From cppreference.net
Concurrency support library
Threads
(C++11)
(C++20)
this_thread namespace
(C++11)
(C++11)
Cooperative cancellation
Mutual exclusion
Generic lock management
Condition variables
(C++11)
Semaphores
Latches and Barriers
(C++20)
(C++20)
Futures
(C++11)
(C++11)
async
(C++11)
Safe reclamation
Hazard pointers
Atomic types
(C++11)
(C++20)
Initialization of atomic types
(C++11) (deprecated in C++20)
(C++11) (deprecated in C++20)
Memory ordering
(C++11) (deprecated in C++26)
Free functions for atomic operations
Free functions for atomic flags
Определено в заголовке <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 , который в конечном итоге будет содержать результат этого вызова функции.

1) Ведёт себя так, как если бы (2) был вызван с policy равным std:: launch :: async | std:: launch :: deferred .
2) Вызывает функцию f с аргументами args в соответствии с определенной политикой запуска policy (см. ниже ).

Возвращаемый тип std::async — это std:: future < V > , где V представляет:

typename std:: result_of < typename std:: decay < F > :: type (
typename std:: decay < Args > :: type ... ) > :: 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 ) ) ,
decay-copy ( std:: forward < Args > ( args ) ) ... )

(до C++23)

std:: invoke ( auto ( std:: forward < F > ( f ) ) ,
auto ( std:: forward < Args > ( args ) ) ... )

(начиная с 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)
  • g является сохраненным значением auto ( std:: forward < F > ( f ) ) и
  • xyz является сохраненной копией auto ( std:: forward < Args > ( args ) ) ... .
(начиная с 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::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]
  1. Возможность конструирования перемещением уже косвенно требуется std::is_constructible_v .

Смотрите также

(C++11)
ожидает значение, которое устанавливается асинхронно
(шаблон класса)
C++ documentation для Execution support library