DispatchersはKotlin CoroutinesにおけるCoroutineContextのコンポーネントで、コルーチンを実行するスレッドを決定します:Main(UIスレッド)、IO(ネットワークとディスク)、Default(CPU負荷の高いタスク)、Unconfined(現在のスレッド)。各ディスパッチャは特定の種類の作業に最適化された専門のスレッドプールを管理します。JetBrainsガイド、2024によると、適切なディスパッチャの選択はアプリケーションのパフォーマンスと安定性にとって重要です。
主要ポイント
DispatchersはCoroutineDispatcherインターフェースの実装であり、CoroutineContextの要素です。これらはコルーチンがどのスレッドまたはスレッドプールで実行されるかを決定します。launchまたはasyncを介してコルーチンを作成する際、ディスパッチャを最初のパラメータとして渡すことができます:launch(Dispatchers.IO) { ... }。ディスパッチャが指定されない場合、外部のCoroutineScopeから継承されます。
Kotlinは4つの組み込みディスパッチャを提供します:Main、IO、Default、Unconfined。各ディスパッチャは特定の種類の操作に最適化された独自のスレッドプールを使用します。適切なディスパッチャの選択がアプリケーションのパフォーマンスを決定します:誤った選択はUIの遅延、CPUコアのアイドル状態、または非効率的なスレッド使用につながります。
| ディスパッチャ | スレッドプール | 最大スレッド数 | 用途 |
|---|---|---|---|
| Dispatchers.Main | 1つ(UI) | 1 | UI更新、LiveData、View |
| Dispatchers.IO | IOプール | 64(limitedParallelism) | ネットワーク、ファイル、DB |
| Dispatchers.Default | CPUプール | Nコア | ソート、パース、計算 |
| Dispatchers.Unconfined | 現在のスレッド | なし | 中間操作、テスト |
Dispatchers.MainはAndroidのメインスレッドでコルーチンを実行するディスパッチャです。UI関連の操作(TextViewの更新、notifyDataSetChangedの呼び出し、LiveDataやStateFlowの操作)向けに設計されています。Androidでは、このディスパッチャはHandler(Looper.getMainLooper())を介して実装されます。
// Correct switch to Main for UI updates
viewModelScope.launch(Dispatchers.IO) {
val data = repository.fetchData()
withContext(Dispatchers.Main) {
_uiState.value = data
}
}
コルーチンが既にMainディスパッチャ上にある場合、追加のwithContext(Dispatchers.Main)はオーバーヘッドを発生させません — ディスパッチャは現在のスレッドを確認し、切り替えをスキップします。withContextはディスパッチャ間の切り替えに推奨される方法です。
Dispatchers.IOはI/O操作用に最適化されたディスパッチャです:HTTPリクエスト(Ktor、OkHttp)、ファイルの読み書き、RoomやSQLDelightの操作。デフォルトで64スレッドのプールを使用し、負荷に応じてスケーリングします。新しいI/Oリクエストごとに、制限に達するまで追加のスレッドを作成できます。
同時I/O操作の数を制御するには、limitedParallelism()を使用します。この関数は並列スレッド数に制限のある新しいディスパッチャを作成し、一括操作中のプール枯渇を防ぎます。
val limitedIo = Dispatchers.IO.limitedParallelism(4)
// Load 100 files with limit of 4 concurrent operations
coroutineScope {
val files = (1..100).map { index ->
async(limitedIo) {
downloadFile("file_$index")
}
}
files.awaitAll()
}
コルーチンが待機に時間を費やす(I/Oバウンド)すべての操作にはIOディスパッチャを使用してください。IOディスパッチャでのCPU負荷の高いタスクは非効率的です — I/O用のスレッドを占有し、システムのスループットを低下させます。
Dispatchers.Defaultはプロセッサに負荷をかける計算操作用のディスパッチャです:ソート、フィルタリング、JSONパース(Moshi、Kotlinx Serialization)、画像処理、計算。プールサイズはプロセッサコア数と同じです(ただし2以上)。これにより、コンテキストスイッチングなしで最大のCPU使用率が保証されます。
suspend fun processData(input: List<RawRecord>): List<ProcessedRecord> {
return withContext(Dispatchers.Default) {
input
.parallelStream()
.map { transform(it) }
.toList()
}
}
I/O操作にDispatchers.Defaultを使用しないでください — 計算タスクを処理できるCPUプールのスレッドをブロックします。IOとDefaultを分離することで、システムリソースの最適な活用が可能になります:IOスレッドはI/Oを待機し、CPUスレッドは常に計算でビジー状態です。
Dispatchers.Unconfinedはコルーチンをいかなるプールにもバインドしない特別なディスパッチャです。コルーチンはlaunch/asyncが呼び出されたスレッドで実行を開始し、一時停止後はresumeを呼び出したスレッドで再開します。この動作は、固定コンテキストを必要としない中間操作に適しています。
fun main() = runBlocking {
launch(Dispatchers.Unconfined) {
println("Before delay: ${Thread.currentThread().getName()}")
delay(500L)
println("After delay: ${Thread.currentThread().getName()}")
}
}
プロダクションコードでは、Dispatchers.Unconfinedはほとんど使用されません。主な使用例:別のディスパッチャにデータを渡す前の軽量変換とテスト。プロダクションのワークロードでは、明示的なディスパッチャを使用してください — Unconfinedは実行スレッドがresumeの実装に依存するため予測できません。
ディスパッチャの選択はタスクの種類によって異なります:UI操作→Main、I/Oバウンド→IO、CPUバウンド→Default、中間→スコープから継承。Androidでは、メインの作業が行われるディスパッチャでコルーチンを起動し、UIを更新する前にwithContextを介してMainに切り替えることをお勧めします。
複雑なシナリオでは、+演算子を使用してディスパッチャを組み合わせます:Dispatchers.IO + SupervisorJob() + CoroutineExceptionHandler。これにより、指定されたディスパッチャ、エラーハンドリング、および分離されたJob階層を持つCoroutineContextが作成されます。
よくある質問
Dispatchers.IOはI/Oバウンド操作(I/O待機)に最大64スレッドのプールを使用しますが、Dispatchers.Defaultは計算タスクにCPUコア数に基づくプールを使用します。スレッドが不足すると、両方のプールが互いにスレッドを共有できます。
はい、シングルスレッドの場合はnewSingleThreadContext()、固定プールの場合はnewFixedThreadPoolContext()を使用してください。プロダクションでは、既存のディスパッチャに基づいてlimitedParallelism()を使用してください — 新しいプールを作成するよりも効率的です。
Dispatchers.Mainが利用できない場合(JUnitテストやバックグラウンドサービスなど)、IllegalStateExceptionがスローされます。テストにはTestCoroutineDispatcher、バックグラウンドサービスにはDispatchers.IOまたはDefaultを使用してください。
Dispatchers.IO.limitedParallelism(N)を使用します。Nは並列スレッドの最大数です。これにより、大量リクエスト時のプール枯渇を防ぎ、制御された並列性を提供します。
Dispatchers.Unconfinedは中間操作に適しています:別のディスパッチャに渡す前の軽量データ変換、テストシナリオ。プロダクションのAndroidコードでは、一時停止後の実行スレッドが未定義のため推奨されません。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。