Spec-Zone.ru › C++

std::async

Определено в заголовке <future>
(1)
template< class Function, class... Args >
std::future<typename std::result_of<typename std::decay<Function>::type(
        typename std::decay<Args>::type...)>::type>
    async( Function&& f, Args&&... args );
(с C++11)
(до C++17)
template< class Function, class... Args >
std::future<std::invoke_result_t<std::decay_t<Function>,
                                 std::decay_t<Args>...>>
    async( Function&& f, Args&&... args );
(с C++17)
(до C++20)
template< class Function, class... Args >
[[nodiscard]] std::future<std::invoke_result_t<std::decay_t<Function>,
                                               std::decay_t<Args>...>>
    async( Function&& f, Args&&... args );
(с C++20)
(2)
template< class Function, class... Args >
std::future<typename std::result_of<typename std::decay<Function>::type(
        typename std::decay<Args>::type...)>::type>
    async( std::launch policy, Function&& f, Args&&... args );
(с C++11)
(до C++17)
template< class Function, class... Args >
std::future<std::invoke_result_t<std::decay_t<Function>,
                                 std::decay_t<Args>...>>
    async( std::launch policy, Function&& f, Args&&... args );
(с C++17)
(до C++20)
template< class Function, class... Args >
[[nodiscard]] std::future<std::invoke_result_t<std::decay_t<Function>,
                                               std::decay_t<Args>...>>
    async( std::launch policy, Function&& f, Args&&... args );
(с C++20)

Шаблон функции std::async выполняет функцию f асинхронно (возможно, в отдельном потоке, который может быть частью пула потоков) и возвращает std::future, который в конечном итоге будет содержать результат этого вызова функции.

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

Вызов std::async синхронизируется (как определено в std::memory_order) с вызовом f, и завершение f предшествует подготовке общего состояния.

Если INVOKE(decay-copy(std::forward<F>(f)),
decay-copy(std::forward<Args>(args))...)
не является допустимым выражением, программа некорректна.

(до C++20)

Программа некорректна, если

  • std::is_constructible_v<std::decay_t<F>, F> равно false,
  • std::is_constructible_v<std::decay_t<Arg_i>, Arg_i> равно false для любого типа Arg_i в Args, или
  • std::is_invocable_v<std::decay_t<F>, std::decay_t<Args>...> равно false.
(с C++20)

Параметры

f - Объект Callable для вызова
args... - параметры для передачи в f
policy - значение битовой маски, где отдельные биты управляют допустимыми методами выполнения
Бит Объяснение
std::launch::async включение асинхронной оценки
std::launch::deferred включение ленивой оценки

Возвращаемое значение

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))...)
как будто в новом потоке выполнения, представленном объектом std::thread.

(до C++23)

std::async вызывает std::invoke(auto(std::forward<F>(f)),
auto(std::forward<Args>(args))...)
как будто в новом потоке выполнения, представленном объектом std::thread.

(с C++23)

Вызовы 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), где
    • g — хранимое значение decay-copy(std::forward<F>(f))(до C++23)auto(std::forward<F>(f))(с C++23) и
    • xyz — сохранённая копия decay-copy(std::forward<Args>(args))...(до C++23)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::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, но переменные состояния потоков не очищаются).

Если 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 неверный тип возвращаемого значения и категория значения
аргументов неясны в отложенном случае
исправленный тип возвращаемого значения и
уточнено, что используются rvalues
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 2752 C++11 std::async возможно не бросит std::bad_alloc если память
для внутренних структур данных не может быть выделена
бросает
LWG 3476 C++11 Function и Args... должны были быть MoveConstructible
в то время как никаких дополнительных движений не указано
требования удалены

См. также

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

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/thread/async

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API