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

  1. Zadeklaruj klasę szablonu o nazwie async_future , która jest sparametryzowana dla typu wynikowego obliczenia. Dodaj public sekcje i private do tej klasy.
template <typename T>
class async_future
{
public:
private:
};
  1. private W sekcji async_future klasy zadeklaruj single_assignment element członkowski task_group danych i .
// Executes the asynchronous work function.
task_group _tasks;

// Stores the result of the asynchronous work function.
single_assignment<T> _value;
  1. public W sekcji async_future klasy zaimplementuj konstruktor. Konstruktor jest szablonem sparametryzowanym w funkcji roboczej, która oblicza wynik. Konstruktor asynchronicznie wykonuje funkcję pracy w elemencie task_group członkowskim danych i używa funkcji concurrency::send , aby zapisać wynik do single_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());
    });
}
  1. public W sekcji async_future klasy zaimplementuj destruktor. Destruktor czeka na zakończenie zadania.
~async_future()
{
   // Wait for the task to finish.
   _tasks.wait();
}
  1. public W sekcji async_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żywacatchtry-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