なぜasyncキーワードが必要なのですか?


19

.Net 4.5でasync / awaitをいじり始めました。私が最初に興味を持っているのは、なぜasyncキーワードが必要なのですか?私が読んだ説明は、マーカーであるため、コンパイラはメソッドが何かを待っていることを知っているというものでした。しかし、コンパイラはキーワードなしでこれを理解できるはずです。それでは、他に何をしますか?


2
ここだ本当に良いブログ記事 F#でC#のキーワードと関連する機能を記述したいくつかの詳細に入る(シリーズ)。
ポール

主に開発者のマーカーであり、コンパイラーのマーカーではないと思います。
CodesInChaos

8
この記事では、それを説明する:blogs.msdn.com/b/ericlippert/archive/2010/11/11/...
svick

@svickありがとう、これが私が探していたものです。今では完全に理にかなっています。
ConditionRacer

回答:


23

ここにはいくつかの答えがあり、それらのすべてが非同期メソッドが何をするかについて話しますが、それらのどれも質問に答えませんasync。そのため、関数宣言に含まれるキーワードとして必要です。

「特別な方法で関数を変換するようコンパイラーに指示すること」ではありません。await単独でそれを行うことができます。どうして?C#には、メソッド本体に特別なキーワードがあるとコンパイラがメソッド本体で極端な(および非常によく似たasync/await)変換を実行する別のメカニズムが既にあるためですyield

それyieldがC#の独自のキーワードではないことを除いて、理由を理解するasyncことも同様に説明します。このメカニズムをサポートするほとんどの言語とは異なり、C#であなたが言うことができないyield value;あなたが言わなければならないyield return value;かわりに。どうして?C#が既に存在した後に言語に追加されたため、どこかで誰かyieldが変数の名前として使用したと仮定することは非常に合理的だったからです。しかし、<variable name> return構文的に正しい既存のシナリオがなかったためyield return、言語に追加され、既存のコードとの100%の後方互換性を維持しながらジェネレーターを導入できるようになりました。

そして、これがasync関数修飾子として追加された理由です:await変数名として使用された既存のコードを壊さないようにします。asyncメソッドがすでに存在しないため、古いコードは無効にならず、新しいコードでは、コンパイラはasyncタグの存在を使用してawait、識別子ではなくキーワードとして扱う必要があることを知ることができます。


3
これは、この記事(コメントで@svickが元々投稿したもの)の内容とほぼ同じですが、回答として投稿した人はいません。わずか2年半かかりました!ありがとう:)
ConditionRacer

将来のバージョンでは、asyncキーワードを指定するための要件が削除される可能性があるという意味ではありませんか?明らかにそれはBCの休憩になりますが、ごく少数のプロジェクトに影響し、アップグレード(変数名の変更など)の簡単な要件になると考えられます。
Quolonelの質問

6

メソッドを通常のメソッドからコールバック付きのオブジェクトに変更します。これには、コード生成にまったく異なるアプローチが必要です

そして、そのような劇的な何かが起こったとき、それを明確に示すのが慣習です(私たちはその教訓をC ++から学びました)


それで、これは本当に読者/プログラマーにとって何かであり、コンパイラにとってはそれほどではありませんか?
ConditionRacer

@ Justin984それは間違いなくコンパイラに特別な何かが起こる必要があることを知らせるのに役立ちます
ラチェットフリーク

コンパイラがなぜそれを必要とするのか興味があります。メソッドを解析して、awaitがメソッド本体のどこかにあることを確認できませんでしたか?
ConditionRacer

複数asyncのsを同時にスケジュールして、次から次へと実行されるのではなく、同時に(少なくともスレッドが使用可能な場合)実行する場合に役立ちます
ラチェットフリーク

@ Justin984:返されるすべてのメソッドがTask<T>実際にメソッドの特性を持っているわけではありません。asyncたとえば、いくつかのビジネス/ファクトリロジックを実行して、他のメソッドを返すように委任するだけTask<T>です。asyncメソッドは戻り値の型を持っているTask<T>署名ませんが、実際に返しTask<T>、彼らはただ返しますTasyncキーワードなしでこれをすべて把握するために、C#コンパイラはメソッドのあらゆる種類の詳細な検査を行う必要があります。
アーロンノート

4

「非同期」や「安全ではない」などのキーワードの全体的な考え方は、変更するコードの処理方法に関するあいまいさを排除することです。asyncキーワードの場合、変更されたメソッドをすぐに返す必要のないものとして扱うようコンパイラーに指示します。これにより、このメソッドが使用されるスレッドは、そのメソッドの結果を待たずに続行できます。事実上、コードの最適化です。


最初の段落は正しいが、割り込みとの比較は間違っている(私の情報では)ので、私はダウン投票したいと思っています。
ポール

私はそれらを目的の点で多少類似していると考えていますが、明確にするために削除します。
ワールドエンジニア

割り込みは私の心の非同期コードよりもイベントのようなものです。通常のコードが再開される前にコンテキスト切り替えを引き起こし、完了するまで実行されます(そして、通常のコードもその実行を完全に無視するかもしれません)呼び出しスレッドが再開される前に完了していません。私はあなたがフードの下で異なる実装を必要とするさまざまなメカニズムについて何を言おうとしていたのかわかりましたが、割り込みはこの点を説明するために私と一緒にジャイブしませんでした。(残りの+1、ところで)。-
ポール

0

OK、これが私の見解です。

何十年も知られているコルーチンと呼ばれるものがあります。( "Knuth and Hopper" -class "for decades")これらはサブルーチンの一般化であり、関数の開始および戻りステートメントで制御を取得および解放するだけでなく、特定のポイント(一時停止ポイント)でも制御を行います。サブルーチンは、中断ポイントのないコルーチンです。

「プロトスレッド」に関する次のペーパーに示すように、Cマクロで実装するのは非常に簡単です。(http://dunkels.com/adam/dunkels06protothreads.pdf)読んでください。待ちます...

これの一番下の行は、マクロがそれぞれの中断ポイントで大きなswitch、およびcaseラベルを作成することです。各一時停止ポイントで、関数はすぐ後に続くcaseラベルの値を保存するため、次に呼び出されたときに実行を再開する場所がわかります。そして、呼び出し元に制御を返します。

これは、「プロトスレッド」で説明されているコードの見かけ上の制御フローを変更することなく行われます。

これらすべての「プロトスレッド」を順番に呼び出す大きなループがあり、単一のスレッドで同時に「プロトスレッド」を実行するとします。

このアプローチには2つの欠点があります。

  1. 再開間でローカル変数の状態を維持することはできません。
  2. 任意の呼び出し深度から「プロトスレッド」を中断することはできません。(すべての中断ポイントはレベル0でなければなりません)

両方に回避策があります。

  1. すべてのローカル変数は、プロトスレッドのコンテキストにプルアップする必要があります(プロトスレッドが次の再開ポイントを保存する必要があるという事実によって既に必要なコンテキスト)
  2. プロトスレッドから別のプロトスレッドを呼び出す必要があると感じる場合は、子プロトスレッドを「スポーン」し、子が完了するまで中断します。

また、マクロと回避策が行う書き換え作業を行うコンパイラーサポートがあれば、意図したとおりにプロトスレッドコードを記述し、キーワードを使用して一時停止ポイントを挿入できます。

そして、これが何でasyncありawait、すべてです:(スタックレス)コルーチンの作成。

C#のコルーチンは、(ジェネリックまたは非ジェネリック)クラスのオブジェクトとして具体化されますTask

これらのキーワードは非常に紛らわしいと思います。私のメンタルリーディングは:

  • async 「サスペンシブル」として
  • await 「完了まで停止」として
  • Task 「未来…」として

今。本当に関数をマークする必要がありますasyncか?関数をコルーチンにするためにコード書き換えメカニズムをトリガーする必要があると言うこととは別に、いくつかのあいまいさを解決します。このコードを検討してください。

public Task<object> AmIACoroutine() {
    var tcs = new TaskCompletionSource<object>();
    return tcs.Task;
}

それasyncが必須ではないと仮定すると、これはコルーチンまたは通常の機能ですか?コンパイラはそれをコルーチンとして書き直すべきかどうか?両方とも、異なる最終的なセマンティクスで可能です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.