Procedura dettagliata: implementazione di future
In questo argomento viene illustrato come implementare future nell'applicazione.Viene inoltre illustrato come combinare le funzionalità esistenti del runtime di concorrenza con funzionalità ancora più avanzate.
Importante |
---|
In questo argomento viene illustrato il concetto di ritardo a scopo dimostrativo.Si consiglia di utilizzare std::future o concurrency::task quando è necessario utilizzare un'attività asincrona che calcola un valore per un utilizzo successivo. |
Un'attività è un calcolo che può essere scomposto in calcoli aggiuntivi più accurati.Una future è un'attività asincrona che calcola un valore per un utilizzo successivo.
Per implementare le future, in questo argomento viene definita la classe async_future.Il async_future classe utilizza i componenti di Runtime della concorrenza: il concurrency::task_group classe e il concurrency::single_assignment classe.La classe async_future utilizza la classe task_group per calcolare un valore in modo asincrono e la classe single_assignment per archiviare il risultato del calcolo.Il costruttore della classe async_future accetta una funzione lavoro che calcola il risultato e il metodo get recupera il risultato.
Per implementare la classe async_future
Dichiarare una classe modello denominata async_future contenente i parametri per il tipo del calcolo risultante.Aggiungere a questa classe le sezioni public e private.
template <typename T> class async_future { public: private: };
Nella sezione private della classe async_future dichiarare un membro dati task_group e single_assignment.
// Executes the asynchronous work function. task_group _tasks; // Stores the result of the asynchronous work function. single_assignment<T> _value;
Nella sezione public della classe async_future implementare il costruttore.Il costruttore è un modello contenente i parametri per la funzione lavoro che calcola il risultato.Il costruttore esegue in modo asincrono la funzione di lavoro nel task_group membro dati e utilizza il concurrency::send funzione per scrivere il risultato per la single_assignment membro dati.
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()); }); }
Nella sezione public della classe async_future implementare il distruttore.Il distruttore attende il completamento dell'attività.
~async_future() { // Wait for the task to finish. _tasks.wait(); }
Nella sezione public della classe async_future implementare il metodo get.Questo metodo utilizza il concurrency::receive funzione per recuperare il risultato della funzione di lavoro.
// 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); }
Esempio
Descrizione
Nell'esempio seguente viene illustrata la classe async_future completa e un esempio del relativo utilizzo.La funzione wmain crea un oggetto std::vector che contiene 10.000 valori interi casuali.Utilizza quindi gli oggetti async_future per trovare i valori minimo e massimo contenuti nell'oggetto vector.
Codice
// 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;
}
Commenti
Questo esempio produce il seguente output:
smallest: 0
largest: 9999
average: 4981
Nell'esempio viene utilizzato il metodo async_future::get per recuperare i risultati del calcolo.Se il calcolo è ancora attivo, il metodo async_future::get ne attende il completamento.
Programmazione robusta
Per estendere la async_future classe per gestire le eccezioni generate dalla funzione di lavoro, modificare il async_future::get metodo da chiamare al concurrency::task_group::wait metodo.Il metodo task_group::wait genera tutte le eccezioni generate dalla funzione lavoro.
Nell'esempio seguente viene illustrata la versione modificata della classe async_future.La funzione wmain utilizza un blocco try-catch per visualizzare il risultato dell'oggetto async_future o per visualizzare il valore dell'eccezione generata dalla funzione lavoro.
// 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;
}
}
Questo esempio produce il seguente output:
caught exception: error
Per ulteriori informazioni sul modello di gestione delle eccezioni nel runtime di concorrenza, vedere Gestione delle eccezioni nel runtime di concorrenza.
Compilazione del codice
Copiare il codice di esempio e incollarlo in un progetto di Visual Studio o incollarlo in un file denominato futures.cpp e quindi eseguire il comando riportato di seguito in una finestra del prompt dei comandi di Visual Studio.
cl.exe /EHsc futures.cpp
Vedere anche
Riferimenti
Concetti
Gestione delle eccezioni nel runtime di concorrenza