auto f2 = std::async(&X::bar, x, "goodbye");←┐ вызывается
│tmpx.bar("goodbye"),
struct Y { │где tmpx — копия x
double operator()(double);
}; │Вызывается tmpy(3.141),
│где tmpy создается
Y y; │из Y перемещающим
auto f3 = std::async(Y(), 3.141)←┘конструктором
auto f4 = std::async(std::ref(y), 2.718);←Вызывается y(2.718)
X baz(X&);
std::async(baz, std::ref(x); ←Вызывается baz(x)
class move_only {
public:
move_only();
move_only(move_only&&);
move_only(move_only const&) = delete;
move_only& operator=(move_only&&);
move_only& operator=(move_only const&) = delete;
void operator()(); │Вызывается tmp(), где tmp
}; │конструируется с помощью
auto f5 = std::async(move_only());←┘std::move(move_only())
По умолчанию реализации предоставлено право решать, запускает ли std::async новый поток или задача работает синхронно, когда программа ожидает будущего результата. В большинстве случаев такое поведение вас устроит, но можно задать требуемый режим в дополнительном параметре std::async перед вызываемой функцией. Этот параметр имеет тип std::launch и может принимать следующие значения: std::launch::deferred — отложить вызов функции до того момента, когда будет вызвана функция-член wait() или get() объекта-будущего; std::launch::async — запускать функцию в отдельном потоке; std::launch::deferred | std::launch::async — оставить решение на усмотрение реализации. Последний вариант подразумевается по умолчанию. В случае отложенного вызова функция может вообще никогда не выполниться. Например:
auto f6 = │Выполнять в
std::async(std::launch::async, Y(), 1.2);←┘новом потоке
auto f7 =
std::async(
std::launch::deferred, baz, std::ref(x)); ←┐
auto f8 = std::async( ←┐│Выполнять
std::launch::deferred | std::launch::async,││при вызове
baz, std::ref(x)); ││wait() или get()
auto f9 = std::async(baz, std::ref(x)); ←┼Оставить на
│усмотрение реализации
f7.wait();←Вызвать отложенную функцию
Ниже в этой главе и далее в главе 8 мы увидим, что с помощью std::async легко разбивать алгоритм на параллельно выполняемые задачи. Однако это не единственный способ ассоциировать объект std::future с задачей; можно также обернуть задачу объектом шаблонного класса std::packaged_task<> или написать код, который будет явно устанавливать значения с помощью шаблонного класса std::promise<>. Шаблон std::packaged_task является абстракцией более высокого уровня, чем std::promise, поэтому начнем с него.
4.2.2. Ассоциирование задачи с будущим результатом