Facebookは先頃,Folly Futureという,既存のstd::future
を機能拡張するC++11ライブラリを導入した。
Futureは並列操作の同期に使用される構成要素である。最初は値の分からない非同期演算の結果を参照する,読み取り専用のプロキシオブジェクトがFutureだ。まだ完了(fulfill)していないFutureの値をクライアントが読み出そうとすると,そのクライアントはブロックされる。ひとつのFutureは通常,ひとつのPromiseに関連付けられている。Futureの値への書き込みアクセスを提供するのがPromiseだ。
以下のコードで示すような非同期操作では,ブロックされることなく,読み取り専用のFutureをすぐに返すことが可能だ。
#include <folly/futures/Future.h>
using folly::Future;
Future<Output> asyncOperation(Input);
Future<Output> f = asyncOperation(input);
asyncOperation
は非同期呼び出しのラッパである。 クライアントは次にFutureにアクセスして,関連するPromise
がisReady()
メソッドを使って完了しているかチェックした上で,value()
を使ってその値を取得する。
Futureはその関連するPromiseから生成されて,setValue()
かsetWith()
のいずれかを使って値を設定することができる。
using folly::Promise;
Future<double> getEnergy(int year) {
auto promise = make_shared<Promise<double>>();
std::thread([=]{
promise->setWith(std::bind(getEnergySync, year));
}).detach();
return promise->getFuture();
}
Folly Futuresが本領を発揮するのはFuture::then
メソッドを使うときだ。コールバックの連鎖が簡単に実現可能であるため,コールバック地獄を防止できる。チェーンがどのように動作するのかを以下に示す。
Future<OutputA> futureA(Output);
Future<OutputB> futureB(OutputA);
Future<OutputC> futureC(OutputB);
OutputD d(OutputC) {
if (somethingExceptional) throw anException;
return OutputD();
}
Future<double> fut =
fooFuture(input)
.then(futureA)
.then(futureB)
.then(futureC)
.then(d)
.then([](OutputD outputD) { // lambdas are ok too
return outputD * M_PI;
});
Folly Futuresの提供する,もうひとつの強力なビルディングブロックがcollect
メソッドだ。Futureのコレクションを,すべてのFutureの完了によって完了状態になる単一のFutureとして扱うことができる。collert
と同じように,次の機能も提供されている。
collectAny
: 入力のFutureのいずれかの完了によって完了する。collectN
: N個のFutureが完了するまで待機する。map
: Futureのコレクションと関数を設定し,各入力Futureのthen()
に対してその関数をコールする。Futureの別の配列を返す。reduce
: Futureのコレクション以外に2つの引数関数(換算値と換算シーケンスの次の値)を取って,それぞれのFutureにその関数を適用する。最後に,Folly Futuresでは,コールバックの実行時に実行コンテキストの概念もサポートされている。executor
オブジェクトをthen
に渡して,そのコールバックをexecutor
経由で行うようなことも可能だ。
struct Executor {
using Func = std::function<void()>;
virtual void add(Func) = 0;
};
a(input).then(executor, b);
Folly Futureの詳しい情報は,資料で見ることができる。