チュートリアル: フューチャの実装
このトピックでは、アプリケーションにフューチャを実装する方法について説明します。 このトピックでは、同時実行ランタイムの既存の機能を組み合わせて、より効果的に使用する方法を示します。
タスクは、より細かい計算に分解することのできる計算です。 フューチャは、後で使用する値を計算する非同期タスクです。
フューチャを実装するために、このトピックでは async_future クラスを定義します。 async_future クラスは、同時実行ランタイムのコンポーネント (Concurrency::task_group クラスおよび Concurrency::single_assignment クラス) を使用します。 async_future クラスは、task_group クラスを使用して非同期的に値を計算し、single_assignment クラスを使用して計算結果を格納します。 async_future クラスのコンストラクターは、結果を計算する処理関数を受け取り、get メソッドが結果を取得します。
future クラスを実装するには
計算結果の型でパラメーター化された async_future という名前のテンプレート クラスを宣言します。 このクラスに public セクションと private セクションを追加します。
template <typename T> class async_future { public: private: };
async_future クラスの private セクションで、task_group と single_assignment データ メンバーを宣言します。
// Executes the asynchronous work function. task_group _tasks; // Stores the result of the asynchronous work function. single_assignment<T> _value;
async_future クラスの public セクションで、コンストラクターを実装します。 コンストラクターは、結果を計算する処理関数でパラメーター化されたテンプレートです。 コンストラクターは 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()); }); }
async_future クラスの public セクションで、デストラクターを実装します。 デストラクターはタスクが完了するまで待機します。
~async_future() { // Wait for the task to finish. _tasks.wait(); }
async_future クラスの public セクションで、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(values.begin(), values.end(), [&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(values.begin(), values.end(), [&](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(values.begin(), values.end(), [&](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(values.begin(), values.end(), 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 2010 のコマンド プロンプト ウィンドウで次のコマンドを実行します。
cl.exe /EHsc futures.cpp