Tutorial: Implementar futuros
En este tema se muestra cómo implementar futuros en la aplicación. En el tema se muestra cómo combinar la funcionalidad existente en el Runtime de simultaneidad para conseguir algo que hace más.
Importante
En este tema se muestra el concepto de futuros para fines de demostración. Se recomienda usar std::future o concurrency::task cuando necesite una tarea asincrónica que calcule un valor para su uso posterior.
Una tarea es un cálculo que se puede descomponer en otros cálculos más específicos. Un futuro es una tarea asincrónica que calcula un valor para su uso posterior.
Para implementar futuros, este tema define la clase async_future
. La clase async_future
usa los siguientes componentes del runtime de simultaneidad: la clase concurrency::task_group y la clase concurrency::single_assignment. La clase async_future
utiliza la clase task_group
para calcular un valor de forma asincrónica y la clase single_assignment
para almacenar el resultado del cálculo. El constructor de la clase async_future
toma una función de trabajo que calcula el resultado, y el método get
recupera el resultado.
Para implementar la clase async_future
- Declare una clase de plantilla con el nombre
async_future
que se parametrice en el tipo del cálculo resultante. Agregue seccionespublic
yprivate
a esta clase.
template <typename T>
class async_future
{
public:
private:
};
- En la sección
private
de la claseasync_future
, declare miembros de datostask_group
ysingle_assignment
.
// Executes the asynchronous work function.
task_group _tasks;
// Stores the result of the asynchronous work function.
single_assignment<T> _value;
- En la sección
public
de la claseasync_future
, implemente el constructor. El constructor es una plantilla que se parametriza en la función de trabajo que calcula el resultado. El constructor ejecuta de forma asincrónica la función de trabajo en el miembro de datostask_group
y usa la función concurrency::send para escribir el resultado en el miembro de datossingle_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());
});
}
- En la sección
public
de la claseasync_future
, implemente el destructor. El destructor espera hasta que finaliza la tarea.
~async_future()
{
// Wait for the task to finish.
_tasks.wait();
}
- En la sección
public
de la claseasync_future
, implemente el métodoget
. Este método usa la función concurrency::receive para recuperar el resultado de la función de trabajo.
// 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);
}
Ejemplo
Descripción
En el siguiente ejemplo se muestran la clase async_future
completa y un ejemplo de su uso. La función wmain
crea un objeto std::vector que contiene 10.000 valores enteros aleatorios. A continuación, utiliza objetos async_future
para encontrar los valores mayor y menor incluidos en el 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;
}
Comentarios
Este ejemplo produce el siguiente resultado:
smallest: 0
largest: 9999
average: 4981
En el ejemplo se usa el método async_future::get
para recuperar los resultado del cálculo. El método async_future::get
espera hasta que finaliza el cálculo si este está todavía activo.
Programación sólida
Para extender la clase async_future
a fin de controlar las excepciones que inicia la función de trabajo, modifique el método async_future::get
para llamar al método concurrency::task_group::wait. El método task_group::wait
inicia las excepciones que genera la función de trabajo.
En el ejemplo siguiente se muestra la versión modificada de la clase async_future
: La función wmain
utiliza un bloque try
-catch
para imprimir el resultado del objeto async_future
o imprimir el valor de la excepción que genera la función de trabajo.
// 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;
}
}
Este ejemplo produce el siguiente resultado:
caught exception: error
Para obtener más información sobre el modelo de control de excepciones en el Runtime de simultaneidad, vea Control de excepciones.
Compilar el código
Copie el código de ejemplo y péguelo en un proyecto de Visual Studio o en un archivo denominado futures.cpp
y, después, ejecute el siguiente comando en una ventana del símbolo del sistema de Visual Studio.
cl.exe /EHsc futures.cpp
Consulte también
Tutoriales del Runtime de simultaneidad
Control de excepciones
task_group (clase)
single_assignment (clase)