私が理解していることから、 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
、同じスレッドでスケジュールされない可能性があることです。は認識されているため、が呼び出されたときに、もしあれば同じ でスケジュールされます。アクティブな がなかった場合、可能性として、別のスレッドで実行されます。Thread1
SynchronizationContext
await
SynchronizationContext
code block 2
SynchronizationContext
MyMethodAsync()
SynchronizationContext
code block 2
最後に、 はasync/await
コンパイラによって作成されたステート マシンに基づいているためyield return
、いくつかの欠点を共有しています。たとえば、ブロックawait
内では実行できません。finally
これで疑問が解消されることを願っています。