Dela via


Genomgång: Implementera terminskontrakt

Det här avsnittet visar hur du implementerar futures i din applikation. Avsnittet visar hur du kombinerar befintliga funktioner i Concurrency Runtime till något som gör mer.

Viktigt!

Det här avsnittet illustrerar begreppet terminer för demonstrationsändamål. Vi rekommenderar att du använder std::future eller concurrency::task när du behöver en asynkron uppgift som beräknar ett värde för senare användning.

En uppgift är en beräkning som kan delas upp i ytterligare, mer detaljerade beräkningar. En framtid är en asynkron uppgift som beräknar ett värde för senare användning.

För att implementera futures-kontrakt definieras klassen i det här avsnittet. Klassen async_future använder dessa komponenter i concurrency Runtime: klassen concurrency::task_group och klassen concurrency::single_assignment . Klassen async_future använder task_group klassen för att beräkna ett värde asynkront och single_assignment klassen för att lagra resultatet av beräkningen. Konstruktorn för async_future klassen tar en arbetsfunktion som beräknar resultatet och get metoden hämtar resultatet.

Implementera klassen async_future

  1. Deklarera en mallklass med namnet async_future som parameteriseras på typen av den resulterande beräkningen. Lägg till public och private avsnitt i den här klassen.
template <typename T>
class async_future
{
public:
private:
};
  1. I avsnittet private i async_future klassen deklarerar du en task_group och en single_assignment datamedlem.
// Executes the asynchronous work function.
task_group _tasks;

// Stores the result of the asynchronous work function.
single_assignment<T> _value;
  1. I avsnittet public i async_future klassen implementerar du konstruktorn. Konstruktorn är en mall som parametriseras på den arbetsfunktion som beräknar resultatet. Konstruktorn kör asynkront arbetsfunktionen i task_group datamedlemmen och använder funktionen concurrency::send för att skriva resultatet till single_assignment datamedlemmen.
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. I avsnittet public i async_future klassen implementerar du destructor. Destructor väntar på att uppgiften ska slutföras.
~async_future()
{
   // Wait for the task to finish.
   _tasks.wait();
}
  1. I avsnittet public i async_future klassen implementerar du get metoden. Den här metoden använder funktionen concurrency::receive för att hämta resultatet av arbetsfunktionen.
// 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); 
}

Exempel

Beskrivning

I följande exempel visas hela async_future klassen och ett exempel på dess användning. Funktionen wmain skapar ett std::vector-objekt som innehåller 10 000 slumpmässiga heltalsvärden. Den använder async_future sedan objekt för att hitta de minsta och största värdena som finns i vector objektet.

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

Kommentarer

Det här exemplet genererar följande utdata:

smallest: 0
largest:  9999
average:  4981

I exemplet används async_future::get metoden för att hämta resultatet av beräkningen. Metoden async_future::get väntar på att beräkningen ska slutföras om beräkningen fortfarande är aktiv.

Robust Programmering

Om du vill utöka async_future klassen för att hantera undantag som genereras av arbetsfunktionen ändrar du async_future::get metoden så att den anropar metoden concurrency::task_group::wait . Metoden task_group::wait kastar alla undantag som genererades av arbetsfunktionen.

I följande exempel visas den ändrade versionen av async_future klassen. Funktionen wmain använder ett try-catch block för att skriva ut resultatet av async_future-objektet eller för att skriva ut värdet för undantaget som genereras av den operativa funktionen.

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

Det här exemplet genererar följande utdata:

caught exception: error

Mer information om undantagshanteringsmodellen i Concurrency Runtime finns i Undantagshantering.

Kompilera koden

Kopiera exempelkoden och klistra in den i ett Visual Studio-projekt, eller klistra in den i en fil med namnet futures.cpp och kör sedan följande kommando i ett Visual Studio-kommandotolkfönster.

cl.exe /EHsc futures.cpp

Se även

Genomgång av samtidighetskörning
undantagshantering
task_group-klass
SingleAssignment-klass