知識マップ: 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まで抜けてアプリが落ちるとしています。
flowchart LR
accTitle: WPF/WinFormsのUIスレッドとasync/awaitの知識マップ
accDescr: UIスレッドがメッセージループを回し続けること、plain awaitが捕まえたSynchronizationContextに継続を戻すこと、ConfigureAwait(false)がその戻りを強制しないこと、Task.RunがCPU計算をUIスレッドから外すこと、Dispatcher・Control.BeginInvoke・InvokeAsyncがUIへ明示的に戻す手段であること、.Result・.Wait()・GetAwaiter().GetResult()がUIスレッドを塞いでデッドロックやフリーズを招きうること、async voidの例外がUIの全体受け皿に届くことの関係を示す図
ui_thread_context["UIスレッドのコンテキスト"]
wpf["WPF"]
windows_forms["Windows Forms"]
message_loop["メッセージループ"]
synchronizationcontext["SynchronizationContext"]
wpf_dispatcher["Dispatcher(WPF)"]
winforms_begininvoke["Control.Invoke / Control.BeginInvoke"]
winforms_invokeasync["Control.InvokeAsync"]
taskcompletionsource["TaskCompletionSource"]
cancellationtoken_dotnet["CancellationToken(.NET)"]
reentrancy_guard["再入防止ガード(Interlocked.Exchange等)"]
cancel_execute_race["キャンセルと実行が競合するレース"]
plain_await["plain await"]
configureawait_false["ConfigureAwait(false)"]
generic_library_code["汎用ライブラリコード"]
taskrun_dotnet["Task.Run"]
io_bound_operation["I/O-bound処理"]
ui_thread_blocking["UIスレッドの詰まり"]
sync_over_async["同期待ちの混入(sync-over-async)"]
deadlock["デッドロック(deadlock)"]
task_result_wait[".Result / .Wait() / .GetAwaiter().GetResult()"]
async_void["async void"]
event_handler_method["イベントハンドラメソッド"]
ui_unhandled_exception_handler["UIの全体受け皿(DispatcherUnhandledException / ThreadException)"]
ui_thread_context -->|"前提とする"| message_loop
wpf -->|"利用する"| synchronizationcontext
windows_forms -->|"利用する"| synchronizationcontext
wpf -->|"利用する"| wpf_dispatcher
windows_forms -->|"利用する"| winforms_begininvoke
windows_forms -.->|"利用する"| winforms_invokeasync
winforms_invokeasync -->|"の後継"| winforms_begininvoke
winforms_begininvoke -.->|"前提とする"| taskcompletionsource
taskcompletionsource -.->|"前提とする"| cancellationtoken_dotnet
taskcompletionsource -.->|"利用する"| reentrancy_guard
reentrancy_guard -->|"防止する"| cancel_execute_race
plain_await -->|"利用する"| synchronizationcontext
plain_await -->|"推奨される対応"| ui_thread_context
configureawait_false -.->|"前提とする"| synchronizationcontext
configureawait_false -->|"用いるのは非推奨"| ui_thread_context
configureawait_false -->|"推奨される対応"| generic_library_code
taskrun_dotnet -->|"推奨される対応"| ui_thread_context
taskrun_dotnet -->|"用いるのは非推奨"| io_bound_operation
taskrun_dotnet -->|"防止する"| ui_thread_blocking
sync_over_async -->|"原因になり得る"| deadlock
task_result_wait -.->|"原因になり得る"| deadlock
task_result_wait -->|"用いるのは非推奨"| ui_thread_context
wpf_dispatcher -->|"原因になり得る"| deadlock
async_void -->|"推奨される対応"| event_handler_method
async_void -->|"原因になり得る"| ui_unhandled_exception_handler
event_handler_method -.->|"前提とする"| ui_thread_context
概念間の関係(全26件)
図と同じ関係を文章でも列挙します。表示している文と機械可読な意味データ(RDFa)は同じ要素に載っています。確度が「確立した関係」のものは直接の関係として、「条件付きの関係」のものは成立条件つきの言明(rdf:Statement)として表現しています。
- UIスレッドのコンテキストはメッセージループを前提とします。
- WPFはSynchronizationContextを利用します。
- Windows FormsはSynchronizationContextを利用します。
- WPFはDispatcher(WPF)を利用します。
- Windows FormsはControl.Invoke / Control.BeginInvokeを利用します。
- Windows FormsはControl.InvokeAsyncを利用します。
- Control.InvokeAsyncはControl.Invoke / Control.BeginInvokeの後継に当たります。
- Control.Invoke / Control.BeginInvokeはTaskCompletionSourceを前提とします。
- TaskCompletionSourceはCancellationToken(.NET)を前提とします。
- TaskCompletionSourceは再入防止ガード(Interlocked.Exchange等)を利用します。
- 再入防止ガード(Interlocked.Exchange等)はキャンセルと実行が競合するレースを防ぎます。
- plain awaitはSynchronizationContextを利用します。
- plain awaitはUIスレッドのコンテキストに対する本記事の推奨です。
- ConfigureAwait(false)はSynchronizationContextを前提とします。
- ConfigureAwait(false)をUIスレッドのコンテキストに用いることは推奨されません。
- ConfigureAwait(false)は汎用ライブラリコードに対する本記事の推奨です。
- Task.RunはUIスレッドのコンテキストに対する本記事の推奨です。
- Task.RunをI/O-bound処理に用いることは推奨されません。
- Task.RunはUIスレッドの詰まりを防ぎます。
- 同期待ちの混入(sync-over-async)はデッドロック(deadlock)の原因になることがあります。
- .Result / .Wait() / .GetAwaiter().GetResult()はデッドロック(deadlock)の原因になることがあります。
- .Result / .Wait() / .GetAwaiter().GetResult()をUIスレッドのコンテキストに用いることは推奨されません。
- Dispatcher(WPF)はデッドロック(deadlock)の原因になることがあります。
- async voidはイベントハンドラメソッドに対する本記事の推奨です。
- async voidはUIの全体受け皿(DispatcherUnhandledException / ThreadException)の原因になることがあります。
- イベントハンドラメソッドはUIスレッドのコンテキストを前提とします。
主要概念の定義
機械可読データ
このページはサイトの知識グラフ(_data/knowledge/)から自動生成されています。誤りの指摘はお問い合わせからお願いします。