私が理解していることから、 await キーワードは、関数が戻るまでコードフローを中断します
はい、いいえ。
- はい、ある意味でコード フローが停止するためです。
- いいえ、このコード フローを実行するスレッドはブロックされないためです。(同期呼び出し
client.GetString()はスレッドをブロックします)。
実際、呼び出し元のメソッドに戻ります。呼び出し元のメソッドに戻ることの意味を理解するために、別の C# コンパイラの魔法であるyield returnステートメントについて読むことができます。
イテレータ ブロックはyield return、メソッドをステート マシンに分割します。ステートメントの後のコードは、列挙子で呼び出されたyield return後にのみ実行されます。MoveNext()(これとこれを参照)。
現在、async/awaitメカニズムも同様のステート マシンに基づいています (ただし、ステート マシンよりもはるかに複雑ですyield return)。
簡単にするために、単純な非同期メソッドを考えてみましょう。
public async Task MyMethodAsync()
{
// code block 1 - code before await
// await stateement
var r = await SomeAwaitableMethodAsync();
// code block 2 - code after await
}
- メソッドを
async識別子でマークすると、コンパイラにメソッドをステートマシンに分割し、awaitこのメソッド内に移動するように指示します。
- コードがスレッドで実行されていて
Thread1、コードが this を呼び出しているとしますMyMethodAsync()。次にcode block 1、同じスレッドで同期的に実行されます。
SomeAwaitableMethodAsync()も同期的に呼び出されますが、メソッドが新しい非同期操作を開始し、Task.
- これが
await絵になるときです。コード フローが呼び出し元に返され、スレッドThread1は呼び出し元のコードを自由に実行できます。メソッドの呼び出しで何が起こるかは、メソッドawaitの呼び出しがオンであるか、他のMyMethodAsync()何かを行うかによって異なりますが、重要なことはThread1ブロックされていないことです。
- 残りの await の魔法 - によって返されたタスクが
SomeAwaitableMethodAsync()最終的に完了すると、実行code block 2がスケジュールされます。
async/awaitはタスク並列ライブラリ上に構築されているため、このスケジューリングは TPL で行われます。
- 問題は、スレッド アフィニティ (WPF/WinForms UI スレッドなど)でアクティブでない限り
code block 2、同じスレッドでスケジュールされない可能性があることです。は認識されているため、が呼び出されたときに、もしあれば同じ でスケジュールされます。アクティブな がなかった場合、可能性として、別のスレッドで実行されます。Thread1SynchronizationContextawaitSynchronizationContextcode block 2SynchronizationContextMyMethodAsync()SynchronizationContextcode block 2
最後に、 はasync/awaitコンパイラによって作成されたステート マシンに基づいているためyield return、いくつかの欠点を共有しています。たとえば、ブロックawait内では実行できません。finally
これで疑問が解消されることを願っています。