Partager via


Procédure pas à pas : implémentation de tâches futures

Cette rubrique indique comment implémenter des tâches futures dans votre application.La rubrique illustre comment combiner les fonctionnalités existantes du runtime d'accès concurrentiel afin d'en étendre et optimiser l'utilisation.

Important

Cette rubrique illustre le concept de début au plus tôt à des fins de démonstration.Nous vous conseillons d'utiliser std::future ou concurrency::task lorsque vous avez besoin d'une tâche asynchrone qui calcule une valeur pour une utilisation ultérieure.

Une tâche est un calcul qui peut être décomposé en plusieurs calculs affinés.Une tâche future est une tâche asynchrone qui calcule une valeur en vue d'une utilisation ultérieure.

Pour implémenter des tâches futures, cette rubrique définit la classe async_future.Le async_future classe utilise ces composants du Runtime d'accès concurrentiel : le concurrency::task_group classe et la concurrency::single_assignment classe.La classe async_future utilise la classe task_group pour calculer une valeur de façon asynchrone et la classe single_assignment pour stocker le résultat du calcul.Le constructeur de la classe async_future prend une fonction de travail qui calcule le résultat et la méthode get extrait le résultat.

Pour implémenter la classe async_future

  1. Déclarez une classe de modèle nommée async_future paramétrable sur le type du calcul résultant.Ajoutez des sections public et private à cette classe.

    template <typename T>
    class async_future
    {
    public:
    private:
    };
    
  2. Dans la section private de la classe async_future, déclarez un task_group et un membre de donnée single_assignment.

    // Executes the asynchronous work function.
    task_group _tasks;
    
    // Stores the result of the asynchronous work function.
    single_assignment<T> _value;
    
  3. Dans la section public de la classe async_future, implémentez le constructeur.Le constructeur est un modèle paramétrable sur la fonction de travail qui calcule le résultat.Le constructeur en mode asynchrone s'exécute la fonction de travail dans la task_group membre de données et utilisations de la concurrency::send fonction pour écrire le résultat dans le single_assignment membre de données.

    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. Dans la section public de la classe async_future, implémentez le destructeur.Le destructeur attend que la tâche se termine.

    ~async_future()
    {
       // Wait for the task to finish.
       _tasks.wait();
    }
    
  5. Dans la section public de la classe async_future, implémentez la méthode get.Cette méthode utilise le concurrency::receive fonction pour récupérer le résultat de la fonction de travail.

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

Exemple

Dd764564.collapse_all(fr-fr,VS.110).gifDescription

L'exemple suivant montre la classe async_future complète et un exemple de son utilisation.La fonction wmain crée un objet std::vector qui contient 10 000 valeurs entières aléatoires.Il utilise ensuite des objets async_future pour rechercher les valeurs les plus petites et les plus grandes contenues dans l'objet vector.

Dd764564.collapse_all(fr-fr,VS.110).gifCode

// 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(fr-fr,VS.110).gifCommentaires

Cet exemple produit la sortie suivante :

smallest: 0
largest:  9999
average:  4981

L'exemple utilise la méthode async_future::get pour extraire les résultats du calcul.La méthode async_future::get attend que le calcul se termine s'il est encore actif.

Programmation fiable

Pour étendre la async_future classe pour gérer les exceptions qui sont levées par la fonction de travail, modifier la async_future::get méthode à appeler le concurrency::task_group::wait méthode.La méthode task_group::wait lève les exceptions générées par la fonction de travail.

L'exemple suivant montre la version modifiée de la classe async_future.La fonction wmain utilise un bloc try-catch pour imprimer le résultat de l'objet async_future ou pour imprimer la valeur de l'exception générée par la fonction de travail.

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

Cet exemple produit la sortie suivante :

caught exception: error

Pour plus d'informations sur le modèle de gestion des exceptions dans le runtime d'accès concurrentiel, consultez Gestion des exceptions dans le runtime d'accès concurrentiel.

Compilation du code

Copiez l'exemple de code, collez-le dans un projet Visual Studio et collez-le dans un fichier nommé futures.cpp , puis exécutez la commande suivante dans une fenêtre d'invite de commande Visual Studio.

cl.exe /EHsc futures.cpp

Voir aussi

Référence

task_group, classe

Classe single_assignment

Concepts

Gestion des exceptions dans le runtime d'accès concurrentiel

Autres ressources

Procédures pas à pas relatives au runtime d'accès concurrentiel