C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

■93834 / 1階層)  async/awaitのWaitAllの挙動について
□投稿者/ Hongliang (957回)-(2020/02/09(Sun) 21:38:28)
一般論として、GUIアプリケーションにおいてWaitを含むコードを実行することは推奨できません。
最終的にはasyncなイベントハンドラでawaitする形を取るようにします。

さて、お書きのコードにおける具体論として。
まずRunTestを呼び出すとUIスレッドでExecuteLongWorkが実行されるわけで、「UIスレッドで時間のかかる処理をしてはいけない」という原則に従っていません。Task.WaitAllも同様です。
Taskを使う場合、入り口まですべてTaskをチェーンすることが望ましいです。
すなわち、
・void RunTest()ではなく、async Task RunTestAsync()に。
・ExecuteLongWork(...)ではなく、await ExecuteLongWorkAsync(...)に。
・Task.WaitAll(...)ではなく、await Task.WhenAll(...)に。
そして、
・async void button1_Click(object sender, EventArgs e)に。
・ts.RunTest(...)ではなく、await ts.RunTestAsync(...)に。

あとまあ大したことではないですが
var result = await Task.Run(() => ...);
これなら
return Task.Run(() => ...)
で十分です。

最後に、なぜ実行が停止するかの解説を。
まず、RunTest時点では実行スレッドはUIスレッドであり、そこでExecuteLongWorkAsyncを呼び出しています。なのでExecuteLongWorkAsyncもUIスレッドで動作します。
ここでTask.Runした後awaitしていますが、UIスレッドから実行したTaskをawaitすると、await以降の処理はUIスレッドで実行されることになります。
// 実現手段としては、UIスレッドで回っているメッセージループに対してメッセージをPOSTしています。
各ExecuteLongWorkAsyncが開始された後、RunTestはUIスレッドですべてのTaskの完了を待ちます。つまりUIスレッドはここで一旦停止します。
各ExecuteLongWorkAsyncは、Taskが完了した後、await以後の処理(return result)をUIスレッドで実行しようとしますが、Task.WaitAllによってUIスレッドが停止中であるため、いつまでたってもreturn resultを実行できません。
各ExecuteLongWorkAsyncがreturn resultできないので、taskListにAddしたTaskはいつまで経っても完了せず、Task.WaitAllはいつまで経っても終わりません。
このように見事なデッドロックが成立してしまっているのです。
編集キー/

前の記事(元になった記事) 次の記事(この記事の返信)
←async/awaitのWaitAllの挙動について /ぱっくん →Re[2]: async/awaitのWaitAllの挙動について /ぱっくん
 
上記関連ツリー

async/awaitのWaitAllの挙動について / ぱっくん (20/02/09(Sun) 15:33) #93833
async/awaitのWaitAllの挙動について / Hongliang (20/02/09(Sun) 21:38) #93834 ←Now
  └ Re[2]: async/awaitのWaitAllの挙動について / ぱっくん (20/02/10(Mon) 13:48) #93845 解決済み

上記ツリーを一括表示 / 上記ツリーをトピック表示
 
上記の記事へ返信