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

  1. Dichiarare una classe modello denominata con async_future parametri sul tipo del calcolo risultante. Aggiungere public sezioni e private a questa classe.
template <typename T>
class async_future
{
public:
private:
};
  1. private Nella sezione della async_future classe dichiarare un task_group oggetto e un single_assignment membro dati.
// Executes the asynchronous work function.
task_group _tasks;

// Stores the result of the asynchronous work function.
single_assignment<T> _value;
  1. public Nella sezione della async_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 nel task_group membro dati e usa la funzione concurrency::send per scrivere il risultato nel 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());
    });
}
  1. public Nella sezione della async_future classe implementare il distruttore. Il distruttore attende il completamento dell'attività.
~async_future()
{
   // Wait for the task to finish.
   _tasks.wait();
}
  1. public Nella sezione della async_future classe implementare il get 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 uncatchtry-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