投稿者 るきお  (社会人) 投稿日時 2023/11/16 19:25:46
> 呼び出し元のプログラムは、別スレッドのAwaitしている処理が終了したところで、呼び出し元のプログラムをその別スレッドに移すことでWaitから抜けられるという理解で宜しいでしょうか。

多くの場合、実行が Await に到達した時点で、内部的にはその時の実行コンテキストが記録されます。Await の処理が終了した後、その記録しておいた実行コンテキストを使って残りの処理を再開しようとします。

この機能により、プログラマーはスレッドのことを特に気にしないで、Await後の処理で元のスレッドの特性(特にWindowsフォームアプリケーション等で重要なのは、元のスレッドでしかGUIの操作ができないという特性)を利用することができます。

参考に乗せた記事のサンプルをコメント部分を日本語訳して引用します。C#ですが、VBとほぼ同じなのでわかると思います。var は Dim です。(ちょっと乱暴な解釈ですが)

private async void button1_Click(object sender, EventArgs e)
{
  // awaitにより同期コンテキスト(SynchronizationContext.Current)が暗黙的に保存されます。
  var data = await webClient.DownloadStringTaskAsync(uri);

  // ここに到達すると、保存された同期コンテキストが実行の再開に使用されます。
  // そのため、何も記事せずUIオブジェクトを操作できます。
}


ところが、元のスレッド側でWaitメソッドやResultメソッドなどで、非同期処理の完了を待機されてしまうと、保存された実行コンテキストで実行を再開しようとしたときにWaitメソッドやResultメソッドの効果で、再開を待たされてしまいます。一方はWaitで待っており、他方はそのせいで再開を待たされており、両方が待つ状態となりデッドロックになります。

ConfigureAwait(False) は、Awaitが元スレッドの実行コンテキストを保存しないようにする効果があります。そのため、非同期処理の完了後、元のスレッドの状態と関係なく実行を再開できます。その代わりにその部分ではWindowsフォームのUIの操作などはできません。

Awaitの処理の後に何も実行すべき処理がないように見えても、まったく何もしないということはありません。たとえば、End Subと書いてあるだけでも、CPUの観点では実行すべきことがいくつかあります。

厳密にはAwaitの対象がTask出ない場合など内部的な動作が異なるケースもあります。

ツェナーさんの、表現は一部理解できないところがありますが、以上のような動作と照らし合わせると正確な理解ではないように思いました。
とは言え、この動作を正確に理解している人はほとんどいないと思います。私もこまかくつっこまれるとちょっと自信がないですね。

参考
https://learn.microsoft.com/ja-jp/archive/msdn-magazine/2011/february/msdn-magazine-parallel-computing-it-s-all-about-the-synchronizationcontext#:~:text=%E5%BE%85%E6%A9%9F%E7%82%B9%E3%81%A7%E7%8F%BE%E5%9C%A8%E3%81%AE%20SynchronizationContext%20%E3%82%92%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A3