Instruções passo a passo: implementando futuros
Este tópico mostra como implementar futuros em seu aplicativo. O tópico demonstra como combinar a funcionalidade existente no Runtime de Simultaneidade a algo que faz ainda mais.
Importante
Este tópico ilustra o conceito de futuros para fins de demonstração. Recomendamos que você use std::future ou concurrency::task quando precisar de uma tarefa assíncrona que calcule um valor para uso posterior.
Uma tarefa é uma computação que pode ser decomposta em cálculos adicionais, mais refinados. Um futuro é uma tarefa assíncrona que calcula um valor para uso posterior.
Para implementar futuros, este tópico define a classe async_future
. A classe async_future
usa esses componentes do Runtime simultâneo: a classe concurrency::task_group e a classe concurrency::single_assignment. A classe async_future
usa a classe task_group
para calcular um valor de forma assíncrona e a classe single_assignment
para armazenar o resultado da computação. O construtor da classe async_future
usa uma função de trabalho que calcula o resultado, e o método get
recupera o resultado.
Para implementar a classe async_future
- Declare uma classe de modelo denominada
async_future
que é parametrizada no tipo da computação resultante. Adicione as seçõespublic
eprivate
a essa classe.
template <typename T>
class async_future
{
public:
private:
};
- Na seção
private
da classeasync_future
, declare um membro de dadostask_group
esingle_assignment
.
// Executes the asynchronous work function.
task_group _tasks;
// Stores the result of the asynchronous work function.
single_assignment<T> _value;
- Na seção
public
da classeasync_future
, implemente o construtor. O construtor é um modelo que é parametrizado na função de trabalho que calcula o resultado. O construtor executa de forma assíncrona a função de trabalho no membro de dadostask_group
e usa a função concurrency::send para gravar o resultado no membro de dadossingle_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());
});
}
- Na seção
public
da classeasync_future
, implemente o destruidor. O destruidor aguarda a conclusão da tarefa.
~async_future()
{
// Wait for the task to finish.
_tasks.wait();
}
- Na seção
public
da classeasync_future
, implemente o métodoget
. Esse método usa a função concurrency::receive para recuperar o resultado da função de trabalho.
// 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);
}
Exemplo
Descrição
O exemplo a seguir mostra a classe completa async_future
e um exemplo de seu uso. A função wmain
cria um objeto std::vector que contém 10.000 valores inteiros aleatórios. Em seguida, ela usa objetos async_future
para localizar os menores e maiores valores contidos no objeto vector
.
Código
// 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;
}
Comentários
Esse exemplo gera a saída a seguir:
smallest: 0
largest: 9999
average: 4981
O exemplo usa o método async_future::get
para recuperar os resultados da computação. O método async_future::get
aguarda a conclusão da computação se a computação ainda estiver ativa.
Programação robusta
Para estender a classe async_future
para lidar com exceções geradas pela função de trabalho, modifique o método async_future::get
para chamar o método concurrency::task_group::wait. O método task_group::wait
gera qualquer exceção gerada pela função de trabalho.
O exemplo a seguir mostra a versão modificada da classe async_future
. A função wmain
usa um bloco try
-catch
para imprimir o resultado do objeto async_future
ou para imprimir o valor da exceção gerada pela função de trabalho.
// 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;
}
}
Esse exemplo gera a saída a seguir:
caught exception: error
Para obter mais informações sobre o modelo de tratamento de exceções no Runtime de Simultaneidade, consulte Tratamento de exceções.
Compilando o código
Copie o código de exemplo e cole-o em um projeto do Visual Studio, ou cole-o em um arquivo chamado futures.cpp
e execute o comando a seguir em uma janela do Prompt de comando do Visual Studio.
cl.exe /EHsc futures.cpp
Confira também
Instruções passo a passo do runtime de simultaneidade
Tratamento de exceção
Classe task_group
Classe single_assignment