Rui:ThreadLibrary:Future

Rui:ThreadLibrary:Future

http://gauche.svn.sourceforge.net/viewvc/gauche/Gauche-threadlib/trunk/concurrent/future.scm

<future>は非同期な計算の結果を表すクラスです。

 (make-future thunk)
 => <future>インスタンス

make-future手続きは即座にfutureオブジェクトを返します。thunkはこの時点では(ほとんどの場合)まだ実行されていません。thunkを実行するためには、future-run!を呼びます。

 (future-run! future-object)

結果を得るためにはfuture-getを呼びます。

 (future-get future-object)
 ;; thunkの返り値が返るか、またはthunkが上げたエラーがraiseする

future-getは、thunkの実行が完了していない場合、その完了を待ってブロックします。

futureの使用例をいくつかあげておきます。まず1つ目は、「Java並行処理プログラミング」であげられていた例です。

n番目のフィボナッチ数を返すWebサービスを作るものとします。大きなフィボナッチ数の計算には時間がかかるので、直前のいくつかの計算結果はキャッシュしておくことにします。計算が終わってその結果がキャッシュに保存されていれば、再度計算する必要がありません。

ただ、このキャッシュには欠点がひとつあります。ほぼ同時に2つの同じリクエストが来た場合、どちらのスレッドもキャッシュを見つけることができず、2つの同じ計算が同時に行われることになります。これは無駄です。この場合、フィボナッチ数の計算をするサンクを<future>でラップしてまずキャッシュに登録し、その後そのfutureオブジェクトをforceするというように改良できます。あとから来たスレッドはfutureオブジェクトをキャッシュから見つけて、それをforceします。これはブロックするかもしれないですし、しないかもしれないですが、同じ計算は二度は行われません。

また別の例は、複数のバックエンドサーバにリクエストを投げてその結果をまとめて使うという話です。

サンクを渡すとfutureを返す、executor手続きを想定してみてください。executorは渡されたサンクを<future>でラップして、それをスレッドプールで実行しようとします。そういうexecutorがあれば、バックエンドに並行にリクエストを投げるためには、たんに必要な問い合わせ処理を行うサンクをexecutorに次々に渡すだけでよくなります。<future>やexecutorを使わず、make-threadなどを直接使ってこのようなプログラムを書くのはやや面倒です。しかしマルチスレッドを使わず、サーバへリクエストを送るたびにその応答を待ってブロックしていたとしたら、ずっと長い時間がかかるかもしれません。

APIは、Javaのjava.util.concurrentパッケージのFutureとFutureTaskをモデルにしています。

More ...