チュートリアル: フューチャの実装

このトピックでは、アプリケーションにフューチャを実装する方法について説明します。 このトピックでは、コンカレンシー ランタイムの既存の機能を組み合わせて、より効果的に使用する方法を示します。

重要

このトピックでは、デモンストレーションのために、将来の概念について説明します。 後で使用できるように値を計算する非同期タスクが必要な場合は、std::future または concurrency::task の使用をお勧めします。

タスクは、より細かい計算に分解することのできる計算です。 フューチャは、後で使用する値を計算する非同期タスクです。

フューチャを実装するために、このトピックでは async_future クラスを定義します。 async_future クラスは同時実行ランタイムのコンポーネントである concurrency::task_group クラスと concurrency::single_assignment クラスを使用します。 async_future クラスは、task_group クラスを使用して非同期的に値を計算し、single_assignment クラスを使用して計算結果を格納します。 async_future クラスのコンストラクターは、結果を計算する処理関数を受け取り、get メソッドが結果を取得します。

async_future クラスを実装するには

  1. 計算結果の型でパラメーター化された async_future という名前のテンプレート クラスを宣言します。 このクラスに public セクションと private セクションを追加します。
template <typename T>
class async_future
{
public:
private:
};
  1. private クラスの async_future セクションで、task_groupsingle_assignment データ メンバーを宣言します。
// Executes the asynchronous work function.
task_group _tasks;

// Stores the result of the asynchronous work function.
single_assignment<T> _value;
  1. public クラスの async_future セクションで、コンストラクターを実装します。 コンストラクターは、結果を計算する処理関数でパラメーター化されたテンプレートです。 コンストラクターは task_group データ メンバーの処理関数を非同期的に実行し、concurrency::send 関数を使用して結果を single_assignment データ メンバーに書き込みます。
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 クラスの async_future セクションで、デストラクターを実装します。 デストラクターはタスクが完了するまで待機します。
~async_future()
{
   // Wait for the task to finish.
   _tasks.wait();
}
  1. public クラスの async_future セクションで、get メソッドを実装します。 このメソッドは concurrency::receive 関数を使用して処理関数の結果を取得します。
// 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); 
}

説明

完全な async_future クラスとその使用例を次に示します。 wmain 関数は、10,000 個のランダムな整数値を含む std::vector オブジェクトを作成します。 次に async_future オブジェクトを使用して、vector オブジェクト内の最小値と最大値を見つけます。

コード

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

コメント

この例を実行すると、次の出力が生成されます。

smallest: 0
largest:  9999
average:  4981

この例では async_future::get メソッドを使用して、計算の結果を取得しています。 async_future::get メソッドは、計算がアクティブな場合は、計算が完了するまで待機します。

信頼性の高いプログラミング

async_future クラスを拡張して作業関数が投げる例外を処理するには、async_future::get メソッドを修正して concurrency::task_group::wait メソッドを呼び出すようにします。 task_group::wait メソッドは、処理関数によって生成されたすべての例外をスローします。

async_future クラスを変更した例を次に示します。 wmain 関数は try-catch ブロックを使用して、async_future オブジェクトの結果を出力するか、処理関数によって生成される例外の値を出力します。

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

この例を実行すると、次の出力が生成されます。

caught exception: error

同時実行ランタイムの例外処理モデルの詳細は、例外処理を参照してください。

コードのコンパイル

コード例をコピーし、Visual Studio プロジェクトに貼り付けるか、futures.cpp という名前のファイルに貼り付けてから、Visual Studio のコマンド プロンプト ウィンドウで次のコマンドを実行します。

cl.exe /EHsc futures.cpp

関連項目

コンカレンシー ランタイムのチュートリアル
例外処理
task_group クラス
single_assignment クラス