Procedura dettagliata: Implementazione di date future
Questo argomento illustra come implementare futures nell'applicazione. L'argomento illustra come combinare le funzionalità esistenti nel runtime di concorrenza in un elemento che esegue di più.
Importante
In questo argomento viene illustrato il concetto di future a scopo dimostrativo. È consigliabile usare std::future o concurrency::task quando è necessaria un'attività asincrona che calcola un valore per un uso successivo.
Un'attività è un calcolo che può essere scomposto in calcoli aggiuntivi con granularità più fine. Un futuro è un'attività asincrona che calcola un valore per un uso successivo.
Per implementare futures, questo argomento definisce la async_future
classe . La async_future
classe usa questi componenti del runtime di concorrenza: la classe concurrency::task_group e la classe concurrency::single_assignment . La async_future
classe usa la task_group
classe per calcolare un valore in modo asincrono e la single_assignment
classe per archiviare il risultato del calcolo. Il costruttore della async_future
classe accetta una funzione di lavoro che calcola il risultato e il get
metodo recupera il risultato.
Per implementare la classe async_future
- Dichiarare una classe modello denominata con
async_future
parametri sul tipo del calcolo risultante. Aggiungerepublic
sezioni eprivate
a questa classe.
template <typename T>
class async_future
{
public:
private:
};
private
Nella sezione dellaasync_future
classe dichiarare untask_group
oggetto e unsingle_assignment
membro dati.
// Executes the asynchronous work function.
task_group _tasks;
// Stores the result of the asynchronous work function.
single_assignment<T> _value;
public
Nella sezione dellaasync_future
classe implementare il costruttore . Il costruttore è un modello con parametri nella funzione di lavoro che calcola il risultato. Il costruttore esegue in modo asincrono la funzione di lavoro neltask_group
membro dati e usa la funzione concurrency::send per scrivere il risultato nelsingle_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());
});
}
public
Nella sezione dellaasync_future
classe implementare il distruttore. Il distruttore attende il completamento dell'attività.
~async_future()
{
// Wait for the task to finish.
_tasks.wait();
}
public
Nella sezione dellaasync_future
classe implementare ilget
metodo . Questo metodo usa la funzione concurrency::receive 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 completa async_future
e un esempio di utilizzo. La wmain
funzione crea un oggetto std::vector che contiene 10.000 valori integer casuali. Usa quindi async_future
oggetti per trovare i valori più piccoli e più grandi 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
Nell'esempio viene prodotto l'output seguente:
smallest: 0
largest: 9999
average: 4981
Nell'esempio viene utilizzato il async_future::get
metodo per recuperare i risultati del calcolo. Il async_future::get
metodo attende il completamento del calcolo se il calcolo è ancora attivo.
Programmazione efficiente
Per estendere la async_future
classe per gestire le eccezioni generate dalla funzione di lavoro, modificare il async_future::get
metodo per chiamare il metodo concurrency::task_group::wait . Il task_group::wait
metodo genera tutte le eccezioni generate dalla funzione di lavoro.
Nell'esempio seguente viene illustrata la versione modificata della async_future
classe . La wmain
funzione utilizza uncatch
try
-blocco per stampare il risultato dell'oggetto async_future
o per stampare il valore dell'eccezione generata dalla funzione di 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;
}
}
Nell'esempio viene prodotto l'output seguente:
caught exception: error
Per altre informazioni sul modello di gestione delle eccezioni nel runtime di concorrenza, vedere Gestione delle eccezioni.
Compilazione del codice
Copiare il codice di esempio e incollarlo in un progetto di Visual Studio oppure incollarlo in un file denominato futures.cpp
e quindi eseguire il comando seguente in una finestra del prompt dei comandi di Visual Studio.
cl.exe /EHsc futures.cpp
Vedi anche
Procedure dettagliate del runtime di concorrenza
Gestione delle eccezioni
Classe task_group
Classe single_assignment