知識マップ: C# async/await実務判断表 - Task.RunとConfigureAwait

記事「C# async/await実務判断表 - Task.RunとConfigureAwait」の主張を、概念と関係(エッジ)に分解した知識グラフの全体です。各関係には根拠・確認日・確度が付いています。

この記事は、C#のasync/awaitで最初に見るべきなのは処理がI/O-boundかCPU-boundかの区別であり、I/O待ちはasync APIをそのままawaitし、CPU計算はUIならTask.Run、ASP.NET Coreのリクエスト処理では避けるべきだと整理します。ConfigureAwait(false)はSynchronizationContextを持たないASP.NET Coreよりも、UIやアプリ固有コンテキストに依存しない汎用ライブラリコードで有力になります。独立した複数のI/O処理はTask.WhenAllやTask.WhenAnyで束ね、件数が多ければParallel.ForEachAsyncやSemaphoreSlimで並列数を制限し、寿命を呼び出し元から切り離したい処理はfire-and-forgetではなくChannel<T>とBackgroundServiceで管理すべきだとしています。

C# async/awaitの実務判断の知識マップI/O-boundかCPU-boundかの見極めがTask.RunやConfigureAwait(false)の使い分け、WhenAll・WhenAny・Channelなどの選択とどうつながるかを示す図です。用いるのは非推奨推奨される対応推奨される対応用いるのは非推奨原因になり得る利用する推奨される対応用いるのは非推奨利用する推奨される対応用いるのは非推奨推奨される対応利用する推奨される対応推奨される対応利用する推奨される対応実装を担う用いるのは非推奨推奨される対応利用する推奨される対応前提とする用いるのは非推奨前提とする推奨される対応I/O-bound処理CPU-bound処理Task.RunUIスレッドのコンテキストASP.NET Coreのリクエスト処理同期待ちの混入(sync-over-async)スレッドプール飢餓(starvation)SynchronizationContextConfigureAwait(false)汎用ライブラリコードCancellationToken(.NET)async voidイベントハンドラメソッド通常の非同期メソッドTask.WhenAllTask.WhenAnyParallel.ForEachAsyncSemaphoreSlimChannelbackpressureBackgroundServiceIHostedServicefire-and-forget呼び出し元から寿命を切り離した背景処理PeriodicTimerIAsyncEnumerableawait usingValueTask

概念間の関係(全26件)

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

主要概念の定義

I/O-bound処理
HTTP、DB、ファイル、ソケットなど、外部の完了待ちが中心の処理。待っている間にスレッドを他の仕事へ返せる。
CPU-bound処理
圧縮、画像処理、ハッシュ計算、重い変換など、CPU計算そのものが中心の処理。どのスレッドで動かすかや並列数の決め方が主題になる。
Task.Run
CPUを使う処理を.NETスレッドプールのワーカースレッドへ明示的にオフロードするためのAPI。I/OバウンドのTaskに使う必要はない。
同期待ちの混入(sync-over-async)
非同期処理の継続やワーカーの中で、同期I/OやTask.Result/Wait()のような同期的な待ちを行ってしまうアンチパターン。スレッドプール飢餓の主因になる。
UIスレッドのコンテキスト
WinForms/WPFなどデスクトップアプリで、UIの応答性を保つために画面更新や重い処理の実行場所として意識されるコンテキスト。
ConfigureAwait(false)
awaitの継続を、捕捉したSynchronizationContextやTaskSchedulerへ投げ返さないことを指示する.NETの記法。「必ずスレッドプールへ移る」保証ではない。
async void
戻り値を持たないasyncメソッド。呼び出し元がawaitできず、完了を待てず、例外処理が難しくテストもしづらい。
Task.WhenAll
複数のTaskを先に全部開始してから、まとめて完了を待つ.NETのAPI。独立した複数処理を直列awaitより効率よくまとめられる。
Task.WhenAny
複数のTaskのうち最初に完了したものを返す.NETのAPI。最初に完了したタスクを返すのであって、最初に成功したタスクを返すのではない。
Parallel.ForEachAsync
MaxDegreeOfParallelismで同時実行数の上限を指定しながら、非同期処理を並列に実行できる.NETのAPI。
SemaphoreSlim
WaitAsyncで非同期に入り、Releaseで抜ける.NETの同期プリミティブ。並列数の上限指定や、awaitをまたぐ排他制御に使う。
Channel<T>
producer/consumerの形をWriteAsync/ReadAsyncで書けるキュー。境界付き(bounded)にするとbackpressureが働く。
BackgroundService
IHostedServiceの書きやすい実装補助となる抽象クラスで、長く走る本体をExecuteAsyncに書ける。
fire-and-forget
呼び出し元が完了を待たない起動方法。例外をどこで見るか、終了時に待つか、件数増加への対応が曖昧になりやすい。
PeriodicTimer
WaitForNextTickAsyncをawaitして使う、一定間隔の非同期処理向けのタイマー型。1つのタイマーに対して同時に複数のWaitForNextTickAsyncを飛ばさない前提で使う。
IAsyncEnumerable<T>
全件そろうまで待たず、届いたものから順に非同期に列挙できる.NETのインターフェース。await foreachと組み合わせて使う。
await using
破棄時にフラッシュや通信終了などの非同期処理が必要な型(IAsyncDisposable)を、非同期に破棄するための構文。
ValueTask
構造体として実装された戻り値型。基本的に1回だけawaitする前提で制約があり、計測して必要性が見えてから選ぶべきとされる。
イベントハンドラメソッド
UIフレームワークがシグネチャ上voidを要求する、ボタンクリックなどのイベントに応答するメソッド。
IHostedService
.NETの汎用ホストが、起動時にStartAsync、停止時にStopAsyncを呼んでくれる仕組み。アプリの寿命に合わせて動く常駐処理の入口。

機械可読データ

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