С правой стороны мы видим
Мы могли бы просто написать следующий код:
auto a (async(launch::async, create, "foo "));
auto b (async(launch::async, create, "bar "));
auto c (async(launch::async, create, "this "));
auto d (async(launch::async, create, "that "));
auto e (async(launch::async, concat, a.get(), b.get()));
auto f (async(launch::async, concat, c.get(), d.get()));
auto g (async(launch::async, twice, e.get()));
auto h (async(launch::async, concat, g.get(), f.get()));
Это хорошее начало для a
, b
, c
и d
, которые представляют четыре подстроки. Они создаются асинхронно в фоновом режиме. К сожалению, этот код блокируется в строке, где мы инициализируем e. Чтобы сконкатенировать a
и b
, нужно вызвать get()
для обеих подстрок, данный код будет get()
. Требуется более хорошая стратегия.
Задействуем сложные вспомогательные функции, которые мы написали. Первая из них — это asynchronize
:
template
static auto asynchronize(F f)
{
return [f](auto ... xs) {
return [=] () {
return async(launch::async, f, xs...);
};
};
}
При наличии функции int f(int, int)
можно сделать следующее:
auto f2 ( asynchronize(f) );
auto f3 ( f2(1, 2) );
auto f4 ( f3() );
int result { f4.get() };
Функция f2
— это наша асинхронная версия функции f
. Ее можно вызвать с теми же аргументами, что и функцию f
, поскольку f2
подражает ей. Затем она возвращает вызываемый объект, который мы сохраняем в f3
. Функция f3
теперь захватывает f
и аргументы 1
, 2
, но пока ничего не вызывает. Это все делается ради захвата.
Теперь при вызове функции f3()
мы наконец получаем объект типа future
, поскольку f3()
делает вызов async(launch::async,f,1,2);
! В некотором смысле семантическое значение f3
заключается в следующем: std::async
Внутреннее лямбда-выражение, которое не принимает никаких аргументов, позволяет пойти по нестандартному пути. С его помощью можно настроить работу для параллельной отправки, но не нужно вызывать никаких блокирующих функций. Мы следуем тому же принципу в гораздо более сложной функции async_adapter
:
template
static auto async_adapter(F f)
{
return [f](auto ... xs) {
return [=] () {
return async(launch::async, fut_unwrap(f), xs()...);
};
};
}
Данная функция также сначала возвращает функцию, которая подражает f
, поскольку принимает те же аргументы. Затем эта функция возвращает вызываемый объект, который тоже не принимает аргументов. В результате упомянутый вызываемый объект наконец отличается от другой вспомогательной функции.
Каково значение строки async(launch::async
, fut_unwrap(f),xs()...);
? Часть xs()...
означает предположение, что все аргументы, которые сохраняются в наборе параметров xs
, являются вызываемыми объектами (как те, что мы постоянно создаем!) и, как следствие, вызываются без аргументов. Эти вызываемые объекты, постоянно создаваемые нами, производят значения типа future
, для которых мы вызываем функцию get()
. Здесь вступает в действие функция fut_unwrap
:
template
{
return [f](auto ... xs) {
return f(xs.get()...);
};
}
Функция fut_unwrap
просто преобразует функцию f
в объект функции, который принимает диапазон аргументов. Данный объект вызывает функцию .get()
для них всех и наконец перенаправляет их к f
.