C#5.0の新しいAsync機能をCall/CCでどのように実装できますか?
-
28-09-2019 - |
質問
私は新しいものに関する新しい発表に従っています async
C#5.0で行われる機能。私は、新しいC#コンパイラがこのスニペットのようなコードに作る継続パススタイルと変換についての基本的な理解を持っています エリック・リパートの投稿:
async void ArchiveDocuments(List<Url> urls)
{
Task archive = null;
for(int i = 0; i < urls.Count; ++i)
{
var document = await FetchAsync(urls[i]);
if (archive != null)
await archive;
archive = ArchiveAsync(document);
}
}
一部の言語は、通話のある通信を介してネイティブに継続を実装することを知っています(callcc
)、しかし、私はそれがどのように機能するのか、それが正確に何をするのか本当に理解していません。
それで、ここに質問があります:Anders et al。弾丸を噛んで実装することを決めていました callcc
の代わりにC#5.0で async
/await
特別なケース、上記のスニペットはどのように見えますか?
解決
元の答え:
あなたの質問は、私が理解しているように、「「待っている」のではなく、タスクベースの非同期に特に「待っている」場合、むしろ、通話のあるコールとのより一般的な制御フロー操作が実装されていたのですか?」
まあ、まず第一に、「待ち望んだ」ことについて考えてみましょう。 「待機」はタイプの表現を取ります Task<T>
, 、待望を取得し、現在の継続で待ち望者に電話します。
await FooAsync()
効果的になります
var task = FooAsync();
var awaiter = task.GetAwaiter();
awaiter.BeginAwait(somehow get the current continuation);
ここで、オペレーターがいるとします callcc
それはその議論としてメソッドを取り、現在の継続でメソッドを呼び出します。それは次のようになります:
var task = FooAsync();
var awaiter = task.GetAwaiter();
callcc awaiter.BeginAwait;
言い換えると:
await FooAsync()
それ以上のものではありません
callcc FooAsync().GetAwaiter().BeginAwait;
それはあなたの質問に答えますか?
更新#1:
コメンターが指摘しているように、以下の答えは、ASYNC/待望の機能の「テクノロジープレビュー」バージョンのコード生成パターンを想定しています。ただし、機能のベータバージョンで実際にわずかに異なるコードを生成しますが、 論理的に 同じです。現在のコードゲンは次のようなものです。
var task = FooAsync();
var awaiter = task.GetAwaiter();
if (!awaiter.IsCompleted)
{
awaiter.OnCompleted(somehow get the current continuation);
// control now returns to the caller; when the task is complete control resumes...
}
// ... here:
result = awaiter.GetResult();
// And now the task builder for the current method is updated with the result.
これはやや複雑であり、すでに計算されている結果を「待っている」ケースを処理します。あなたが待っている結果が実際にあなたのためにすでにあなたのためにメモリにキャッシュされている場合、あなたが中断したところから再び拾い上げて、あなたが中断したところから再び拾うというすべてのリガマロールを通過する必要はありません。
したがって、「待望」と「CallCC」の接続は、プレビューリリースほど簡単ではありませんが、Wainterの「コンプレット」方法で本質的にCallCCを行っていることはまだ明らかです。必要がない場合は、CallCCを実行しません。
更新#2:
この答えとして
https://stackoverflow.com/a/9826822/88656
Timwiが指摘すると、Call/CCの意味論と待望はまったく同じではありません。 「真の」コール/CCは、私たちが「キャプチャ」することを要求します 全体 メソッドの継続 コールスタック全体を含みます, 、または同等に、プログラム全体が継続パッシングスタイルに書き直されること。
「待機」機能は、「協力的なコール/CC」に似ています。継続は「現在のもの」とのみをキャプチャします タスク回復方法 待っている時点で次にやろうとしていますか?」 発信者 タスクを返す方法は、タスクが完了した後、何か面白いことをします。 その継続 タスクの継続として。
他のヒント
私は継続の専門家ではありませんが、Async/waingとCall/CCの違いを説明することに刺します。もちろん、この説明は、私がCall/CCとAsync/待ち望を理解していると仮定していますが、それは私がそうするかどうかはわかりません。それにもかかわらず、ここに行きます...
C# 'async'を使用すると、コンパイラに、その状態をヒープダタ構造に瓶詰めする方法を理解する特定の方法の特別なバージョンを生成するように指示しているため、「実際のスタックから削除」して後で再開できます。 Async-Context内では、「待機」は「Call/CC」のようになります。これは、コンパイラが生成したオブジェクトを使用して状態を瓶詰めし、タスクが完了するまで「実際のスタック」を降りるという点で「Call/CC」に似ています。ただし、状態を瓶詰めすることを可能にするのは、非同期メソッドのコンパイラ書き換えであるため、Asyncコンテキスト内でのみ使用できます。
ファーストクラスのコール/CCでは、言語ランタイムはすべてのコードを生成し、現在の継続をコールコンティネーション機能に瓶詰めすることができます(非同期キーワードを不要にします)。 Call/CCは引き続き待ち望んでいるように振る舞い、現在のコンテンツ状態(スタック状態を考えてください)をボットアップし、呼び出された機能として関数として渡されます。これを行う1つの方法は、ヒープフレームを使用することです すべて 「スタック」フレームの代わりに関数の呼び出し。 (「スタックレスPython」または多くのスキーム実装のように、「スタックレス」と呼ばれることもあります)別の方法は、すべてのデータを「実際のスタック」から削除し、コール/CCターゲットを呼び出す前にヒープデータ構造に詰めることです。 。
スタックで混合された外部関数(Dllimportを考える)への呼び出しがある場合、いくつかのトリッキーな問題が発生する可能性があります。これが、彼らがAsync/待望の実装で行った理由だと思います。
http://www.madore.org/~david/computers/callcc.html
C#では、これらのメカニズムを使用するために関数を「非同期」としてマークする必要があるため、この非同期キーワードは多くのライブラリの多くの機能に広がるウイルスになるのではないかと思います。これが起こった場合。彼らは最終的に、このコンパイラ生解ベースのASYNCモデルではなく、VMレベルでファーストクラスのコール/CCを実装すべきだと気付くかもしれません。時間だけが教えてくれます。ただし、現在のC#環境のコンテキストでは、確かに有用なツールです。
ちょっと無意味だと思います。スキーム通訳者は、多くの場合、地方の状態をヒープに捉える状態マシンでコール/CCを実装します。それはそうです まさに C#5.0(またはより正確にはC#2.0の反復ター)も同様に行うこと。彼ら やりました Call/CCを実装すると、彼らが思いついた抽象化は非常にエレガントです。