Пошаговое руководство. Реализация фьючерсов
В этом разделе показано, как реализовать фьючерсы в приложении. В этом разделе показано, как объединить существующие функции в среде выполнения параллелизма в то, что делает больше.
Внимание
В этом разделе рассматривается понятие фьючерсов для демонстрационных целей. Рекомендуется использовать std::future или concurrency::task, если требуется асинхронная задача , которая вычисляет значение для последующего использования.
Задача — это вычисление, которое можно разложить на дополнительные, более подробные вычисления. Будущее — это асинхронная задача, которая вычисляет значение для последующего использования.
Для реализации будущих версий async_future
этот раздел определяет класс. Класс async_future
использует эти компоненты среды выполнения параллелизма: класс concurrency::task_group и класс concurrency::single_assignment . Класс async_future
использует task_group
класс для асинхронного вычисления значения и single_assignment
класса для хранения результата вычисления. Конструктор async_future
класса принимает рабочую функцию, вычисляющую результат, и get
метод получает результат.
Реализация класса async_future
- Объявите класс шаблона с именем
async_future
, параметризованный по типу результирующего вычисления. Добавьтеpublic
иprivate
разделы в этот класс.
template <typename T>
class async_future
{
public:
private:
};
private
В разделеasync_future
класса объявите элементtask_group
данных иsingle_assignment
элемент данных.
// Executes the asynchronous work function.
task_group _tasks;
// Stores the result of the asynchronous work function.
single_assignment<T> _value;
public
В разделеasync_future
класса реализуйте конструктор. Конструктор — это шаблон, параметризованный в рабочей функции, вычисляющей результат. Конструктор асинхронно выполняет рабочую функцию вtask_group
члене данных и использует функцию параллелизма::send для записи результата вsingle_assignment
элемент данных.
template <class Functor>
explicit async_future(Functor&& fn)
{
// Execute the work function in a task group and send the result
// to the single_assignment object.
_tasks.run([fn, this]() {
send(_value, fn());
});
}
public
В разделеasync_future
класса реализуйте деструктор. Деструктор ожидает завершения задачи.
~async_future()
{
// Wait for the task to finish.
_tasks.wait();
}
public
В разделеasync_future
класса реализуйтеget
метод. Этот метод использует функцию параллелизма::receive для получения результата рабочей функции.
// Retrieves the result of the work function.
// This method blocks if the async_future object is still
// computing the value.
T get()
{
return receive(_value);
}
Пример
Description
В следующем примере показан полный async_future
класс и пример его использования. Функция wmain
создает объект std::vector , содержащий 10 000 случайных целых чисел. Затем он использует async_future
объекты для поиска наименьших и крупнейших значений, содержащихся в объекте vector
.
Код
// futures.cpp
// compile with: /EHsc
#include <ppl.h>
#include <agents.h>
#include <vector>
#include <algorithm>
#include <iostream>
#include <numeric>
#include <random>
using namespace concurrency;
using namespace std;
template <typename T>
class async_future
{
public:
template <class Functor>
explicit async_future(Functor&& fn)
{
// Execute the work function in a task group and send the result
// to the single_assignment object.
_tasks.run([fn, this]() {
send(_value, fn());
});
}
~async_future()
{
// Wait for the task to finish.
_tasks.wait();
}
// Retrieves the result of the work function.
// This method blocks if the async_future object is still
// computing the value.
T get()
{
return receive(_value);
}
private:
// Executes the asynchronous work function.
task_group _tasks;
// Stores the result of the asynchronous work function.
single_assignment<T> _value;
};
int wmain()
{
// Create a vector of 10000 integers, where each element
// is between 0 and 9999.
mt19937 gen(2);
vector<int> values(10000);
generate(begin(values), end(values), [&gen]{ return gen()%10000; });
// Create a async_future object that finds the smallest value in the
// vector.
async_future<int> min_value([&]() -> int {
int smallest = INT_MAX;
for_each(begin(values), end(values), [&](int value) {
if (value < smallest)
{
smallest = value;
}
});
return smallest;
});
// Create a async_future object that finds the largest value in the
// vector.
async_future<int> max_value([&]() -> int {
int largest = INT_MIN;
for_each(begin(values), end(values), [&](int value) {
if (value > largest)
{
largest = value;
}
});
return largest;
});
// Calculate the average value of the vector while the async_future objects
// work in the background.
int sum = accumulate(begin(values), end(values), 0);
int average = sum / values.size();
// Print the smallest, largest, and average values.
wcout << L"smallest: " << min_value.get() << endl
<< L"largest: " << max_value.get() << endl
<< L"average: " << average << endl;
}
Комментарии
В примере получается следующий вывод.
smallest: 0
largest: 9999
average: 4981
В примере используется async_future::get
метод для получения результатов вычисления. Метод async_future::get
ожидает завершения вычисления, если вычисление по-прежнему активно.
Отказоустойчивость
Чтобы расширить async_future
класс для обработки исключений, создаваемых рабочей функцией, измените async_future::get
метод для вызова метода параллелизма::task_group::wait . Метод task_group::wait
создает все исключения, созданные рабочей функцией.
В следующем примере показана измененная версия async_future
класса. Функция wmain
используетcatch
try
-блок для печати результата async_future
объекта или для печати значения исключения, созданного рабочей функцией.
// futures-with-eh.cpp
// compile with: /EHsc
#include <ppl.h>
#include <agents.h>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace concurrency;
using namespace std;
template <typename T>
class async_future
{
public:
template <class Functor>
explicit async_future(Functor&& fn)
{
// Execute the work function in a task group and send the result
// to the single_assignment object.
_tasks.run([fn, this]() {
send(_value, fn());
});
}
~async_future()
{
// Wait for the task to finish.
_tasks.wait();
}
// Retrieves the result of the work function.
// This method blocks if the async_future object is still
// computing the value.
T get()
{
// Wait for the task to finish.
// The wait method throws any exceptions that were generated
// by the work function.
_tasks.wait();
// Return the result of the computation.
return receive(_value);
}
private:
// Executes the asynchronous work function.
task_group _tasks;
// Stores the result of the asynchronous work function.
single_assignment<T> _value;
};
int wmain()
{
// For illustration, create a async_future with a work
// function that throws an exception.
async_future<int> f([]() -> int {
throw exception("error");
});
// Try to read from the async_future object.
try
{
int value = f.get();
wcout << L"f contains value: " << value << endl;
}
catch (const exception& e)
{
wcout << L"caught exception: " << e.what() << endl;
}
}
В примере получается следующий вывод.
caught exception: error
Дополнительные сведения о модели обработки исключений в среде выполнения параллелизма см. в разделе "Обработка исключений".
Компиляция кода
Скопируйте пример кода и вставьте его в проект Visual Studio или вставьте его в файл с именем futures.cpp
, а затем выполните следующую команду в окне командной строки Visual Studio.
cl.exe /EHsc futures.cpp
См. также
Пошаговые руководства по среде выполнения с параллелизмом
Обработка исключений
Класс task_group
Класс single_assignment