Exemplarische Vorgehensweise: Implementieren von Futures
In diesem Thema erfahren Sie, wie Sie Futures in Ihre Anwendung implementieren. Es wird veranschaulicht, wie Sie die vorhandenen Funktionen in der Concurrency Runtime kombinieren können, um mehr Funktionalität zu erzielen.
Wichtig
In diesem Thema wird das Konzept von Futures zu Demonstrationszwecken veranschaulicht. Es wird empfohlen, "std::future" oder "concurrency::task" zu verwenden, wenn Sie eine asynchrone Aufgabe benötigen, die einen Wert für die spätere Verwendung berechnet.
Bei einer Aufgabe handelt es sich um eine Berechnung, die in zusätzliche, feinkörnige Berechnungen zerlegt werden kann. Eine Zukunft ist eine asynchrone Aufgabe, die einen Wert für die spätere Verwendung berechnet.
Zur Implementierung von Futures wird in diesem Thema die async_future
-Klasse definiert. Die async_future
Klasse verwendet diese Komponenten der Concurrency Runtime: die Concurrency::task_group-Klasse und die Concurrency::single_assignment-Klasse . Die async_future
-Klasse verwendet die task_group
-Klasse zur asynchronen Berechnung eines Werts und die single_assignment
-Klasse zum Speichern des Ergebnisses. Der Konstruktor der async_future
-Klasse akzeptiert eine Arbeitsfunktion, die das Ergebnis berechnet, das mit der get
-Methode abgerufen wird.
So implementieren Sie die async_future-Klasse
- Deklarieren Sie eine Vorlagenklasse mit dem Namen
async_future
, die auf der Grundlage des Typs der resultierenden Berechnung parametrisiert wird. Fügen Sie der Klasse einenpublic
-Abschnitt und einenprivate
-Abschnitt hinzu.
template <typename T>
class async_future
{
public:
private:
};
- Deklarieren Sie im
private
-Abschnitt derasync_future
-Klasse eintask_group
-Datenmember und einsingle_assignment
-Datenmember.
// Executes the asynchronous work function.
task_group _tasks;
// Stores the result of the asynchronous work function.
single_assignment<T> _value;
- Implementieren Sie im
public
-Abschnitt derasync_future
-Klasse den Konstruktor. Der Konstruktor ist eine Vorlage, die auf Grundlage der Arbeitsfunktion, die zur Berechnung des Ergebnisses dient, parametrisiert wird. Der Konstruktor führt die Arbeitsfunktion imtask_group
Datenmemm asynchron aus und verwendet die Parallelität::Send-Funktion , um das Ergebnis in dassingle_assignment
Datenelement zu schreiben.
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());
});
}
- Implementieren Sie im
public
-Abschnitt derasync_future
-Klasse den Destruktor. Der Destruktor wartet auf das Beenden der Aufgabe.
~async_future()
{
// Wait for the task to finish.
_tasks.wait();
}
- Implementieren Sie im
public
-Abschnitt derasync_future
-Klasse dieget
-Methode. Diese Methode verwendet die Funktion "concurrency::receive " zum Abrufen des Ergebnisses der Arbeitsfunktion.
// 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);
}
Beispiel
BESCHREIBUNG
Das folgende Beispiel zeigt die vollständige async_future
-Klasse mit einer Verwendungsmöglichkeit. Die wmain
Funktion erstellt ein std::vector-Objekt , das 10.000 zufällige ganzzahlige Werte enthält. Anschließend werden mithilfe von async_future
-Objekten der kleinste und der größte Wert im vector
-Objekt gesucht.
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;
}
Kommentare
Dieses Beispiel erzeugt die folgende Ausgabe:
smallest: 0
largest: 9999
average: 4981
Im Beispiel werden die Ergebnisse der Berechnung mit der async_future::get
-Methode abgerufen. Die async_future::get
-Methode wartet, bis die Berechnung beendet ist.
Stabile Programmierung
Um die async_future
Klasse so zu erweitern, dass Ausnahmen verarbeitet werden, die von der Arbeitsfunktion ausgelöst werden, ändern Sie die async_future::get
Methode, um die Parallelität::task_group::wait-Methode aufzurufen. Die task_group::wait
-Methode löst alle von der Arbeitsfunktion generierten Ausnahmen aus.
Das folgende Beispiel zeigt die geänderte Version der async_future
-Klasse. Die wmain
Funktion verwendet einen try
-catch
Block zum Drucken des Ergebnisses des async_future
Objekts oder zum Drucken des Werts der Ausnahme, die von der Arbeitsfunktion generiert wird.
// 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;
}
}
Dieses Beispiel erzeugt die folgende Ausgabe:
caught exception: error
Weitere Informationen zum Ausnahmebehandlungsmodell in der Parallelitätslaufzeit finden Sie unter Exception Handling.
Kompilieren des Codes
Kopieren Sie den Beispielcode, fügen Sie ihn in ein Visual Studio-Projekt ein, oder fügen Sie ihn in eine Datei ein, die benannt futures.cpp
ist, und führen Sie dann den folgenden Befehl in einem Visual Studio-Eingabeaufforderungsfenster aus.
cl.exe /EHsc futures.cpp
Siehe auch
Exemplarische Vorgehensweisen für die Concurrency Runtime
Ausnahmebehandlung
task_group-Klasse
single_assignment-Klasse