Procédure pas à pas : implémentation d’objets future
Cette rubrique montre comment implémenter des futures dans votre application. La rubrique montre comment combiner des fonctionnalités existantes dans le runtime d’accès concurrentiel en quelque chose qui fait plus.
Important
Cette rubrique illustre le concept d’avenir à des fins de démonstration. Nous vous recommandons 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 calculs plus précis et plus précis. Un avenir est une tâche asynchrone qui calcule une valeur pour une utilisation ultérieure.
Pour implémenter des futures, cette rubrique définit la async_future
classe. La async_future
classe utilise ces composants du runtime d’accès concurrentiel : la classe concurrency ::task_group et la classe concurrency ::single_assignment . La async_future
classe utilise la task_group
classe pour calculer une valeur de manière asynchrone et la single_assignment
classe pour stocker le résultat du calcul. Le constructeur de la async_future
classe prend une fonction de travail qui calcule le résultat et la get
méthode récupère le résultat.
Pour implémenter la classe async_future
- Déclarez une classe de modèle nommée
async_future
paramétrable sur le type du calcul résultant. Ajoutezpublic
etprivate
sections à cette classe.
template <typename T>
class async_future
{
public:
private:
};
- Dans la
private
section de laasync_future
classe, déclarez un membre de données et untask_group
single_assignment
membre de données.
// Executes the asynchronous work function.
task_group _tasks;
// Stores the result of the asynchronous work function.
single_assignment<T> _value;
- Dans la
public
section de laasync_future
classe, implémentez le constructeur. Le constructeur est un modèle paramétrable sur la fonction de travail qui calcule le résultat. Le constructeur exécute de façon asynchrone la fonction de travail dans le membre detask_group
données et utilise la fonction concurrency ::send pour écrire le résultat dans le membre desingle_assignment
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());
});
}
- Dans la
public
section de laasync_future
classe, implémentez le destructeur. Le destructeur attend la fin de la tâche.
~async_future()
{
// Wait for the task to finish.
_tasks.wait();
}
- Dans la
public
section de laasync_future
classe, implémentez laget
méthode. Cette méthode utilise la fonction concurrency ::receive 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
Description
L’exemple suivant montre la classe complète async_future
et un exemple de son utilisation. La wmain
fonction crée un objet std ::vector qui contient 10 000 valeurs entières aléatoires. Il utilise ensuite des async_future
objets pour rechercher les valeurs les plus petites et les plus importantes contenues dans l’objet vector
.
Code
// 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;
}
Commentaires
Cet exemple produit la sortie suivante :
smallest: 0
largest: 9999
average: 4981
L’exemple utilise la async_future::get
méthode pour récupérer les résultats du calcul. La async_future::get
méthode attend que le calcul se termine si le calcul est toujours actif.
Programmation fiable
Pour étendre la async_future
classe pour gérer les exceptions levées par la fonction de travail, modifiez la async_future::get
méthode pour appeler la méthode concurrency ::task_group ::wait . La task_group::wait
méthode lève toutes les exceptions générées par la fonction de travail.
L’exemple suivant montre la version modifiée de la async_future
classe. La wmain
fonction utilise un try
-catch
bloc 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.
Compilation du code
Copiez l’exemple de code et collez-le dans un projet Visual Studio, ou collez-le dans un fichier nommé futures.cpp
, puis exécutez la commande suivante dans une fenêtre d’invite de commandes Visual Studio.
cl.exe /EHsc futures.cpp
Voir aussi
Procédures pas à pas relatives au runtime d’accès concurrentiel
Gestion des exceptions
Classe task_group
single_assignment, classe