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.

Nota importanteImportante

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

  1. 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:
    };
    
  2. 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;
    
  3. 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());
        });
    }
    
  4. 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();
    }
    
  5. 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

Dd764564.collapse_all(it-it,VS.110).gifDescrizione

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.

Dd764564.collapse_all(it-it,VS.110).gifCodice

// 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;
}

Dd764564.collapse_all(it-it,VS.110).gifCommenti

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

Classe task_group

Classe single_assignment

Concetti

Gestione delle eccezioni nel runtime di concorrenza

Altre risorse

Procedure dettagliate del runtime di concorrenza