知識マップ: WPF/WinFormsのasyncとUIスレッドを一枚で整理

記事「WPF/WinFormsのasyncとUIスレッドを一枚で整理」の主張を、概念と関係(エッジ)に分解した知識グラフの全体です。各関係には根拠・確認日・確度が付いています。

この記事は、WPF/WinFormsのUIスレッドがメッセージループを回し続けることを土台に、plain awaitはその時点のSynchronizationContextを捕まえて継続をUIスレッドへ戻すため素のままUIを更新できる一方、ConfigureAwait(false)はその戻りを強制しないので汎用ライブラリコードに向くと整理します。Task.RunはCPU計算だけをUIスレッドから外す道具であり、UI以外の場所からUIへ戻すにはWPFのDispatcher、WinFormsのControl.BeginInvokeや.NET 9以降のControl.InvokeAsyncを使うべきだとします。逆に.Result・.Wait()・.GetAwaiter().GetResult()でUIスレッドを同期的に塞ぐと継続がUIへ戻れずデッドロックやフリーズを招きかねず、async voidのイベントハンドラで例外を握らなければWPFのDispatcherUnhandledExceptionやWinFormsのThreadExceptionまで抜けてアプリが落ちるとしています。

WPF/WinFormsのUIスレッドとasync/awaitの知識マップUIスレッドがメッセージループを回し続けること、plain awaitが捕まえたSynchronizationContextに継続を戻すこと、ConfigureAwait(false)がその戻りを強制しないこと、Task.RunがCPU計算をUIスレッドから外すこと、Dispatcher・Control.BeginInvoke・InvokeAsyncがUIへ明示的に戻す手段であること、.Result・.Wait()・GetAwaiter().GetResult()がUIスレッドを塞いでデッドロックやフリーズを招きうること、async voidの例外がUIの全体受け皿に届くことの関係を示す図前提とする利用する利用する利用する利用する利用するの後継前提とする前提とする利用する防止する利用する推奨される対応前提とする用いるのは非推奨推奨される対応推奨される対応用いるのは非推奨防止する原因になり得る原因になり得る用いるのは非推奨原因になり得る推奨される対応原因になり得る前提とするUIスレッドのコンテキストWPFWindows FormsメッセージループSynchronizationContextDispatcher(WPF)Control.Invoke / Control.BeginInvokeControl.InvokeAsyncTaskCompletionSourceCancellationToken(.NET)再入防止ガード(Interlocked.Exchange等)キャンセルと実行が競合するレースplain awaitConfigureAwait(false)汎用ライブラリコードTask.RunI/O-bound処理UIスレッドの詰まり同期待ちの混入(sync-over-async)デッドロック(deadlock).Result / .Wait() / .GetAwaiter().GetResult()async voidイベントハンドラメソッドUIの全体受け皿(DispatcherUnhandledException / ThreadException)

概念間の関係(全26件)

図と同じ関係を文章でも列挙します。表示している文と機械可読な意味データ(RDFa)は同じ要素に載っています。確度が「確立した関係」のものは直接の関係として、「条件付きの関係」のものは成立条件つきの言明(rdf:Statement)として表現しています。

主要概念の定義

UIスレッドのコンテキスト
WinForms/WPFなどデスクトップアプリで、UIの応答性を保つために画面更新や重い処理の実行場所として意識されるコンテキスト。
WPF
XAMLでUIを宣言的に記述する、Windows専用のデスクトップUIフレームワーク。ベクターベースの描画とデータバインディングを特徴とする。
Windows Forms
デスクトップGUIアプリケーションを構築するための.NET/.NET FrameworkのUIフレームワーク。
Control.InvokeAsync
.NET 9で追加された、WinFormsのControlがUIスレッドへ処理を投げるためのAPI。Taskを返しasyncフローと素直に噛み合う。
Control.Invoke / Control.BeginInvoke
WinFormsでUIスレッドへ処理を投げる旧来のAPI。Invokeは同期送信で呼び出し側を待たせ、BeginInvokeは投稿してすぐ返るが戻り値はIAsyncResultでそのままawaitできない。
TaskCompletionSource
完了していないTaskを手動で作成し、後からTrySetResult/TrySetException/TrySetCanceledで結果・例外・キャンセルを確定させる.NETのクラス。イベントベースのAPIをTaskベースの非同期パターンへ橋渡しする際に使う。
再入防止ガード(Interlocked.Exchange等)
Interlocked.Exchangeなどの原子的操作でフラグを立て、同じ処理が並行して重複実行されるのを防ぐ実装パターン。
plain await
ConfigureAwait(false)を付けない既定のawait。その時点のSynchronizationContext.Current(無ければTaskScheduler.Current)を捕まえ、完了後の継続をそこへ戻そうとする。
ConfigureAwait(false)
awaitの継続を、捕捉したSynchronizationContextやTaskSchedulerへ投げ返さないことを指示する.NETの記法。「必ずスレッドプールへ移る」保証ではない。
Task.Run
CPUを使う処理を.NETスレッドプールのワーカースレッドへ明示的にオフロードするためのAPI。I/OバウンドのTaskに使う必要はない。
同期待ちの混入(sync-over-async)
非同期処理の継続やワーカーの中で、同期I/OやTask.Result/Wait()のような同期的な待ちを行ってしまうアンチパターン。スレッドプール飢餓の主因になる。
.Result / .Wait() / .GetAwaiter().GetResult()
Taskの完了を同期的に待つ3つの手段の総称。呼び出し元のスレッドをブロックする点は共通で、違うのは主に例外の包まれ方だけである。
Dispatcher(WPF)
WPFのUIスレッド上で処理を順番に実行するためのメッセージキューを管理するクラス。DispatcherTimerのTickもこのキューに積まれる。
async void
戻り値を持たないasyncメソッド。呼び出し元がawaitできず、完了を待てず、例外処理が難しくテストもしづらい。
イベントハンドラメソッド
UIフレームワークがシグネチャ上voidを要求する、ボタンクリックなどのイベントに応答するメソッド。

機械可読データ

このページはサイトの知識グラフ(_data/knowledge/)から自動生成されています。誤りの指摘はお問い合わせからお願いします。