.Net 4.5でasync / awaitをいじり始めました。私が最初に興味を持っているのは、なぜasyncキーワードが必要なのですか?私が読んだ説明は、マーカーであるため、コンパイラはメソッドが何かを待っていることを知っているというものでした。しかし、コンパイラはキーワードなしでこれを理解できるはずです。それでは、他に何をしますか?
.Net 4.5でasync / awaitをいじり始めました。私が最初に興味を持っているのは、なぜasyncキーワードが必要なのですか?私が読んだ説明は、マーカーであるため、コンパイラはメソッドが何かを待っていることを知っているというものでした。しかし、コンパイラはキーワードなしでこれを理解できるはずです。それでは、他に何をしますか?
回答:
ここにはいくつかの答えがあり、それらのすべてが非同期メソッドが何をするかについて話しますが、それらのどれも質問に答えません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
、識別子ではなくキーワードとして扱う必要があることを知ることができます。
async
キーワードを指定するための要件が削除される可能性があるという意味ではありませんか?明らかにそれはBCの休憩になりますが、ごく少数のプロジェクトに影響し、アップグレード(変数名の変更など)の簡単な要件になると考えられます。
メソッドを通常のメソッドからコールバック付きのオブジェクトに変更します。これには、コード生成にまったく異なるアプローチが必要です
そして、そのような劇的な何かが起こったとき、それを明確に示すのが慣習です(私たちはその教訓をC ++から学びました)
async
のsを同時にスケジュールして、次から次へと実行されるのではなく、同時に(少なくともスレッドが使用可能な場合)実行する場合に役立ちます
Task<T>
実際にメソッドの特性を持っているわけではありません。async
たとえば、いくつかのビジネス/ファクトリロジックを実行して、他のメソッドを返すように委任するだけTask<T>
です。async
メソッドは戻り値の型を持っているTask<T>
に署名ませんが、実際に返しTask<T>
、彼らはただ返しますT
。async
キーワードなしでこれをすべて把握するために、C#コンパイラはメソッドのあらゆる種類の詳細な検査を行う必要があります。
「非同期」や「安全ではない」などのキーワードの全体的な考え方は、変更するコードの処理方法に関するあいまいさを排除することです。asyncキーワードの場合、変更されたメソッドをすぐに返す必要のないものとして扱うようコンパイラーに指示します。これにより、このメソッドが使用されるスレッドは、そのメソッドの結果を待たずに続行できます。事実上、コードの最適化です。
OK、これが私の見解です。
何十年も知られているコルーチンと呼ばれるものがあります。( "Knuth and Hopper" -class "for decades")これらはサブルーチンの一般化であり、関数の開始および戻りステートメントで制御を取得および解放するだけでなく、特定のポイント(一時停止ポイント)でも制御を行います。サブルーチンは、中断ポイントのないコルーチンです。
「プロトスレッド」に関する次のペーパーに示すように、Cマクロで実装するのは非常に簡単です。(http://dunkels.com/adam/dunkels06protothreads.pdf)読んでください。待ちます...
これの一番下の行は、マクロがそれぞれの中断ポイントで大きなswitch
、およびcase
ラベルを作成することです。各一時停止ポイントで、関数はすぐ後に続くcase
ラベルの値を保存するため、次に呼び出されたときに実行を再開する場所がわかります。そして、呼び出し元に制御を返します。
これは、「プロトスレッド」で説明されているコードの見かけ上の制御フローを変更することなく行われます。
これらすべての「プロトスレッド」を順番に呼び出す大きなループがあり、単一のスレッドで同時に「プロトスレッド」を実行するとします。
このアプローチには2つの欠点があります。
両方に回避策があります。
また、マクロと回避策が行う書き換え作業を行うコンパイラーサポートがあれば、意図したとおりにプロトスレッドコードを記述し、キーワードを使用して一時停止ポイントを挿入できます。
そして、これが何でasync
ありawait
、すべてです:(スタックレス)コルーチンの作成。
C#のコルーチンは、(ジェネリックまたは非ジェネリック)クラスのオブジェクトとして具体化されますTask
。
これらのキーワードは非常に紛らわしいと思います。私のメンタルリーディングは:
async
「サスペンシブル」としてawait
「完了まで停止」としてTask
「未来…」として今。本当に関数をマークする必要がありますasync
か?関数をコルーチンにするためにコード書き換えメカニズムをトリガーする必要があると言うこととは別に、いくつかのあいまいさを解決します。このコードを検討してください。
public Task<object> AmIACoroutine() {
var tcs = new TaskCompletionSource<object>();
return tcs.Task;
}
それasync
が必須ではないと仮定すると、これはコルーチンまたは通常の機能ですか?コンパイラはそれをコルーチンとして書き直すべきかどうか?両方とも、異なる最終的なセマンティクスで可能です。