3. Теперь реализуем три функции, которые преобразуют строки. Первая функция создаст объект типа std::string
static string create(const char *s)
{
pcout{} << "3s CREATE " << quoted(s) << '\n';
this_thread::sleep_for(3s);
return {s};
}
4. Следующая функция принимает два строковых объекта и возвращает их сконкатенированный вариант. Мы будем приостанавливать ее на 5 секунд, чтобы симулировать сложность выполнения этой задачи:
static string concat(const string &a, const string &b)
{
pcout{} << "5s CONCAT "
<< quoted(a) << " "
<< quoted(b) << '\n';
this_thread::sleep_for(5s);
return a + b;
}
5. Последняя функция, наполненная вычислениями, принимает строку и конкатенирует ее с самой собой. На это потребуется 3 секунды:
static string twice(const string &s)
{
pcout{} << "3s TWICE " << quoted(s) << '\n';
this_thread::sleep_for(3s);
return s + s;
}
6. Теперь можно использовать эти функции в последовательной программе, но мы же хотим элегантно ее распараллелить! Так что реализуем некоторые вспомогательные функции.
asynchronize
принимает функцию f
и возвращает вызываемый объект, который захватывает ее. Можно вызвать данный объект, передав ему любое количество аргументов, и он захватит их вместе с функцией f
в другой вызываемый объект, который будет возвращен. Этот последний вызываемый объект может быть вызван без аргументов. Затем он вызывает функцию f
асинхронно со всеми захваченными им аргументами:template
static auto asynchronize(F f)
{
return [f](auto ... xs) {
return [=] () {
return async(launch::async, f, xs...);
};
};
}
7. Следующая функция будет использоваться функцией, которую мы объявим на шаге 8. Она принимает функцию f
future
. Затем он вызовет функцию .get()
для всех этих объектов, применит к ним функцию f
и вернет результат:template
static auto fut_unwrap(F f)
{
return [f](auto ... xs) {
return f(xs.get()...);
};
}
8. Последняя вспомогательная функция также принимает функцию f
f
. Такой вызываемый объект может быть вызван с любым количеством аргументов, представляющих собой вызываемые объекты, которые он возвращает вместе с f
в другом вызываемом объекте. Этот итоговый вызываемый объект можно вызвать без аргументов. Он вызывает все вызываемые объекты, захваченные в наборе xs...
. Они возвращают объекты типа future
, которые нужно распаковать с помощью функции fut_unwrap
. Распаковка объектов типа future
и применение самой функции f
для реальных значений, находящихся в объектах типа future
, происходит асинхронно с помощью std::async
:template
static auto async_adapter(F f)
{
return [f](auto ... xs) {
return [=] () {
return async(launch::async,
fut_unwrap(f), xs()...);
};
};
}
9. О’кей, возможно, предыдущие фрагменты кода были несколько запутанными и напоминали фильм
create
, concat
и twice
и сделаем их асинхронными. Функция async_adapter
заставляет обычную функцию ожидать получения аргументов типа future
и возвращает в качестве результата объект типа future
. Она похожа на оболочку, преобразующую синхронный мир в асинхронный. Необходимо использовать функцию asynchronize
для функции create
, поскольку она будет возвращать объект типа future
, но следует передать ей реальные значения. Цепочка зависимостей для задач должна начинаться с вызовов create
:int main()
{
auto pcreate (asynchronize(create));
auto pconcat (async_adapter(concat));
auto ptwice (async_adapter(twice));