概要
Ver. 4.0
予定
先物(future)
継続(continuation)
・先物「待ち時間を無駄にしない」という意味での非同期処理では、いったん非同期に処理したうえで、処理結果の値を使って次の処理を始めたいことが多々あるTask の場合、ジェネリック引数付きの Task<T> を使って、非同期処理の結果を受け取ることができるTask<T> みたいな、「非同期に処理して、処理が終わったら値を取りたい」みたいなものを先物(future: 将来的に受け取る物)って読んだりする。(実際、他の言語だと Future って名前のクラスになってたりする.NET 4 の Task でも、プレビュー版のころは Future<T> って名前になってた)var t = Task.Factory.StartNew(() =>{ Thread.Sleep(3000); return 10;});Console.WriteLine(t.Result);みたいなコード書くと、t.Result の時点でタスク終了待ちに入って、3秒後にタスクの戻り値(この場合10)が取れる。(スレッド単位のシーケンスがどうなってるか絵にしたい)main 別スレッド||-------→|| || || ||←-------|この間、メイン スレッド止まる|・継続ただ、↑ みたいに3秒待ってしまうと非同期の意味ない。実際には、「その処理終わったら引き続きこういう処理して」というようなデリゲートを渡す。Task.Factory.StartNew(() =>{ Thread.Sleep(3000); return 10;}).ContinueWith(t =>{Console.WriteLine(t.Result);});こういう、「引き続き処理して」ってのを継続(continuation)って呼ぶ。(スレッド単位のシーケンスがどうなってるか絵にしたい)main 別スレッド||-------→| |ここから先、メイン スレッドは自由 | | |→| 継続 |・継続と戻り値論理的にはほぼ一緒。ただ、書き方がだいぶ煩わしく・・・↓表にする------------------------------通常の戻り値受け渡し側(関数内): return x;受け取り側(呼ぶ側): var result = Method();継続渡し受け渡し側: continuation(x);受け取り側: Method(x => ...);------------------------------戻り値を受け取る代わりに、継続処理をデリゲートで渡すreturn で戻り値を返す代わりに、継続処理を呼び出す再帰が深くなりそうに見えるけど、末尾呼び出しなので最適化可能。・Combinator戻り値⇔継続渡し の変換ができれば、非同期処理を同期っぽく書ける。if とか while とかの制御構文も、If 関数とか While 関数作って、継続渡し化可能。(こういうのを Combinator って言うらしい?関数型言語の用語。)実際、F# の let! 非同期処理はこの 戻り値⇔継続渡し 変換で実現してるみたい。http://tomasp.net/blog/async-compilation-internals.aspx(この方法、正常フローの間はまだいいけども、例外処理絡むとどんどん面倒に)・余談.NET Framework は末尾呼び出しの最適化用の補助命令持ってる(Tail 命令)メソッド呼び出し命令の前に Tail を付けると、スタック解放してから次のメソッド呼び出ししてくれる。F# とかではこの Tail 命令入るし、Tail 命令が吐いてなくても、.NET 4の64ビット版では自動的に末尾呼び出しの最適化してくれる。(Release ビルド時のみ)