Wskazówki: wdrażanie przyszłych operacji
W tym temacie pokazano, jak zaimplementować kontrakty terminowe w aplikacji. W tym temacie pokazano, jak połączyć istniejące funkcje w środowisku uruchomieniowym współbieżności w coś, co robi więcej.
Ważne
W tym temacie przedstawiono koncepcję kontraktów terminowych na potrzeby demonstracyjne. Zalecamy użycie polecenia std::future lub concurrency::task , gdy jest wymagane zadanie asynchroniczne, które oblicza wartość do późniejszego użycia.
Zadanie to obliczenie, które można rozdzielić na dodatkowe, bardziej szczegółowe obliczenia. Przyszłość to zadanie asynchroniczne, które oblicza wartość do późniejszego użycia.
Aby zaimplementować kontrakty terminowe, w tym temacie zdefiniowano klasę async_future
. Klasa async_future
używa tych składników środowiska uruchomieniowego współbieżności: klasy concurrency::task_group i klasy concurrency::single_assignment . Klasa async_future
używa task_group
klasy do obliczenia wartości asynchronicznie i single_assignment
klasy do przechowywania wyniku obliczeń. Konstruktor async_future
klasy przyjmuje funkcję pracy, która oblicza wynik, a get
metoda pobiera wynik.
Aby implementować klasę async_future
- Zadeklaruj klasę szablonu o nazwie
async_future
, która jest sparametryzowana dla typu wynikowego obliczenia. Dodajpublic
sekcje iprivate
do tej klasy.
template <typename T>
class async_future
{
public:
private:
};
private
W sekcjiasync_future
klasy zadeklarujsingle_assignment
element członkowskitask_group
danych i .
// Executes the asynchronous work function.
task_group _tasks;
// Stores the result of the asynchronous work function.
single_assignment<T> _value;
public
W sekcjiasync_future
klasy zaimplementuj konstruktor. Konstruktor jest szablonem sparametryzowanym w funkcji roboczej, która oblicza wynik. Konstruktor asynchronicznie wykonuje funkcję pracy w elemencietask_group
członkowskim danych i używa funkcji concurrency::send , aby zapisać wynik dosingle_assignment
elementu członkowskiego danych.
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());
});
}
public
W sekcjiasync_future
klasy zaimplementuj destruktor. Destruktor czeka na zakończenie zadania.
~async_future()
{
// Wait for the task to finish.
_tasks.wait();
}
public
W sekcjiasync_future
klasy zaimplementuj metodęget
. Ta metoda używa funkcji concurrency::receive , aby pobrać wynik funkcji pracy.
// 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);
}
Przykład
opis
W poniższym przykładzie przedstawiono kompletną async_future
klasę i przykład użycia. Funkcja wmain
tworzy obiekt std::vector zawierający 10 000 losowych wartości całkowitych. Następnie używa async_future
obiektów do znajdowania najmniejszych i największych wartości zawartych w vector
obiekcie.
Kod
// 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;
}
Komentarze
Ten przykład generuje następujące wyniki:
smallest: 0
largest: 9999
average: 4981
W przykładzie użyto async_future::get
metody w celu pobrania wyników obliczeń. Metoda async_future::get
czeka na zakończenie obliczeń, jeśli obliczenia są nadal aktywne.
Niezawodne programowanie
Aby rozszerzyć klasę tak async_future
, aby obsługiwała wyjątki zgłaszane przez funkcję roboczą, zmodyfikuj async_future::get
metodę , aby wywołać metodę concurrency::task_group::wait . Metoda task_group::wait
zgłasza wszelkie wyjątki wygenerowane przez funkcję pracy.
W poniższym przykładzie przedstawiono zmodyfikowaną wersję async_future
klasy. Funkcja wmain
używacatch
try
-bloku, aby wydrukować wynik async_future
obiektu lub wyświetlić wartość wyjątku wygenerowanego przez funkcję roboczą.
// 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;
}
}
Ten przykład generuje następujące wyniki:
caught exception: error
Aby uzyskać więcej informacji na temat modelu obsługi wyjątków w środowisku uruchomieniowym współbieżności, zobacz Obsługa wyjątków.
Kompilowanie kodu
Skopiuj przykładowy kod i wklej go w projekcie programu Visual Studio lub wklej go w pliku o nazwie futures.cpp
, a następnie uruchom następujące polecenie w oknie wiersza polecenia programu Visual Studio.
cl.exe /EHsc futures.cpp
Zobacz też
Środowisko uruchomieniowe współbieżności — wskazówki
Obsługa wyjątków
task_group, klasa
single_assignment, klasa