はじめに
― マルチタスクのように見せる「同時進行の仕組み」 ―
1. 並行処理とは?
並行処理(Concurrency) とは、
複数の処理を「同時に進行しているように見せる」仕組みです。
実際には CPU は 1つのタスクを高速に切り替えながら動かしており、
人間の目には同時進行のように見えます。
例えるなら
一人のシェフが、
「スープを温めながら」「サラダを切り」「肉を焼く」
― 少しずつ同時進行しているように見える状態。
2. 並行処理と並列処理の違い
| 比較項目 | 並行処理(Concurrency) | 並列処理(Parallelism) |
|---|---|---|
| CPU構成 | 1コアでも可能 | 複数コアが必要 |
| 実行の仕組み | タスクを高速に切り替える | 複数のタスクを同時実行 |
| 主な用途 | ネット通信、ファイル操作など I/O 待機 | 数値計算、暗号化などCPU負荷処理 |
| Kotlin Dispatcher | Dispatchers.IO |
Dispatchers.Default |
| 体感 | 同時に動いているように見える | 実際に同時に動く |
3. Kotlin における並行処理の考え方
Kotlin では Coroutine(コルーチン) を使って、
スレッドを直接操作せずに「軽量な並行処理」を実現できます。
特徴
- 数千単位のコルーチンを1スレッドで実行可能
- I/O待ち(ネット通信・DB操作)中に他の処理を進められる
- CPUリソースを無駄にしない
4. シンプルな並行処理の例
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000)
println("🍜 ラーメンが完成!")
}
launch {
delay(500)
println("🥗 サラダが完成!")
}
println("👨🍳 調理開始!")
}
実行結果
👨🍳 調理開始!
🥗 サラダが完成!
🍜 ラーメンが完成!
launchにより2つの処理が「並行」に進行します。
ただし、これは「並列」ではなく、スレッド切り替えによる同時進行です。
5. I/O処理に強い理由
並行処理は「待ち時間のある処理」に特に有効です。
例えば、複数のAPIを同時に叩く場合:
import kotlinx.coroutines.*
suspend fun fetchUser() : String {
delay(1000)
return "User Data"
}
suspend fun fetchOrders() : String {
delay(1000)
return "Order List"
}
fun main() = runBlocking {
val start = System.currentTimeMillis()
val user = async(Dispatchers.IO) { fetchUser() }
val orders = async(Dispatchers.IO) { fetchOrders() }
println("${user.await()} & ${orders.await()}")
println("処理時間: ${System.currentTimeMillis() - start}ms")
}
実行結果
User Data & Order List
処理時間: 約1000ms
それぞれのAPIが並行に進むため、
本来2秒かかる処理が 約1秒 で完了します。
6. KotlinのDispatcherで見る並行性
| Dispatcher | 主な用途 | 並行 or 並列 |
|---|---|---|
Dispatchers.IO |
I/O待機処理(通信、DB、ファイル) | 並行処理 |
Dispatchers.Default |
CPU計算系タスク | 並列処理 |
Dispatchers.Main |
Android UI操作 | シングルスレッド |
newSingleThreadContext("MyThread") |
明示的に単一スレッド | シリアル処理 |
Dispatchers.IOは、内部的には数十スレッドを切り替えながら
I/O待機を効率的に管理します。
7. 並行処理で注意すべき点
| 問題 | 説明 | 対策 |
|---|---|---|
| 競合状態(Race Condition) | 複数タスクが同じデータに同時アクセス |
Mutex や Atomic クラスを使用 |
| キャンセル漏れ | スコープ外のタスクが中断されない |
CoroutineScope を正しく設計 |
| リソース枯渇 | 無制限にタスクを並行実行 |
Semaphore で同時実行数を制限 |
| エラーハンドリング | 親子関係で例外が伝播 |
SupervisorScope を利用 |
8. 構造化並行性(Structured Concurrency)
Kotlinのコルーチンでは、
「構造化並行性(Structured Concurrency)」 という設計原則が導入されています。
意味:
コルーチンのライフサイクルを「スコープ(Scope)」で管理し、
親タスクが終了すれば子タスクも確実に終了する仕組み。
suspend fun loadData() = coroutineScope {
val user = async { fetchUser() }
val orders = async { fetchOrders() }
println("${user.await()} + ${orders.await()}")
}
これにより、キャンセルや例外が発生しても安全に並行処理を終了できます。
9. 並行処理の実用例
- 複数のAPIを同時に呼び出し、結果をまとめる
- ファイルの一括ダウンロード
- データベース・キャッシュ・ネットワークを同時問い合わせ
- Androidで「画像読み込み+ネット更新+アニメーション」を同時に行う
10. 図解で理解する並行処理の流れ
実際のスレッドは1つでも、
OSが切り替えながら「同時進行のように」見せています。
まとめ
| 要点 | 内容 |
|---|---|
| 並行処理とは | タスクを交互に進めて「同時に動いているように見せる」 |
| 並列処理との違い | 並列=物理的同時、並行=論理的同時 |
| Kotlinでの手段 | コルーチン + Dispatchers.IO
|
| 利点 | I/O待機を効率化し、応答性を高める |
| 注意点 | スコープ管理とリソース制御が重要 |