メインコンテンツにスキップ

🚀 なぜコルーチンなのか?

📖 コルーチンとは?

**コルーチン(Coroutines)**は、非同期プログラミングを簡単かつ直感的にするKotlinの中核機能です。複雑なコールバック地獄から抜け出し、まるで同期コードのように書くことができます!

💡 従来の非同期の問題

コールバック地獄

// ❌ コールバック方式(読みにくくメンテナンスが困難)
fun fetchUser(userId: String, callback: (User?) -> Unit) {
fetchFromNetwork(userId) { result ->
if (result != null) {
fetchProfile(result.id) { profile ->
if (profile != null) {
fetchPosts(profile.id) { posts ->
callback(User(result, profile, posts))
}
} else {
callback(null)
}
}
} else {
callback(null)
}
}
}

スレッドのコスト

// ❌ スレッドはコストが高い
fun downloadFiles() {
repeat(1000) {
Thread {
// 各スレッドはメモリを大量に使用
downloadFile("file_$it.txt")
}.start()
}
// メモリ不足の可能性!
}

✨ コルーチンの利点

1. 簡潔なコード

// ✅ コルーチン(読みやすく直感的)
suspend fun fetchUser(userId: String): User? {
val result = fetchFromNetwork(userId) ?: return null
val profile = fetchProfile(result.id) ?: return null
val posts = fetchPosts(profile.id)
return User(result, profile, posts)
}

2. 軽量な同時実行

// ✅ 数千個のコルーチンでも問題なし
suspend fun downloadFiles() {
coroutineScope {
repeat(10000) {
launch {
// 軽量なコルーチン!
downloadFile("file_$it.txt")
}
}
}
}

3. 構造化された同時実行

// ✅ 自動的にライフサイクル管理
suspend fun processData() {
coroutineScope {
val job1 = launch { task1() }
val job2 = launch { task2() }

// すべてのタスクが終了すると自動的に完了
}
}

🎯 実際の使用例

ネットワークリクエスト

// コールバックなしでスッキリ!
suspend fun loadUserData(userId: String): UserData {
val user = apiService.getUser(userId)
val posts = apiService.getPosts(userId)
val friends = apiService.getFriends(userId)

return UserData(user, posts, friends)
}

UI更新

// メインスレッドをブロックせずに
suspend fun updateUI() {
val data = withContext(Dispatchers.IO) {
// バックグラウンドでデータをロード
loadDataFromDatabase()
}

// メインスレッドでUI更新
updateViews(data)
}

並列処理

// 複数のタスクを同時に!
suspend fun fetchAllData(): CombinedData = coroutineScope {
val users = async { fetchUsers() }
val products = async { fetchProducts() }
val orders = async { fetchOrders() }

CombinedData(
users.await(),
products.await(),
orders.await()
)
}

🔥 コルーチン vs スレッド

特徴コルーチンスレッド
重さ非常に軽量重い
生成コストほぼなし高い
個数数万個可能数千個が限界
切り替えコスト低い高い
キャンセル簡単困難

パフォーマンス比較

// コルーチン:100,000個実行
suspend fun testCoroutines() {
val time = measureTimeMillis {
coroutineScope {
repeat(100_000) {
launch {
delay(1000)
}
}
}
}
println("コルーチン: ${time}ms") // ~1000ms
}

// スレッド:10,000個だけでも...
fun testThreads() {
val time = measureTimeMillis {
repeat(10_000) {
Thread {
Thread.sleep(1000)
}.start()
}
}
println("スレッド: ${time}ms") // メモリ不足の可能性!
}

🎨 コルーチンの核心概念

Suspend(一時停止)

// suspend関数はコルーチン内でのみ呼び出し可能
suspend fun doWork() {
delay(1000) // スレッドをブロックせずに待機
println("作業完了")
}

構造化された同時実行

// 親子関係でライフサイクル管理
fun main() = runBlocking {
launch { // 親
launch { // 子1
delay(1000)
println("子1完了")
}
launch { // 子2
delay(2000)
println("子2完了")
}
println("親は子が終わるまで待機")
}
}

🤔 よくある質問

Q1. コルーチンは新しいスレッドですか?

A: いいえ!コルーチンはスレッド上で動作する軽量な作業単位です。

// 1つのスレッドで複数のコルーチンを実行可能
fun main() = runBlocking {
repeat(3) { i ->
launch {
println("コルーチン $i: ${Thread.currentThread().name}")
}
}
}
// すべて同じスレッドで実行される可能性があります!

Q2. すべての関数をsuspendにする必要がありますか?

A: いいえ!非同期処理が必要な関数のみsuspendにします。

// ❌ 不要
suspend fun add(a: Int, b: Int) = a + b

// ✅ 必要
suspend fun fetchData() = withContext(Dispatchers.IO) {
loadFromNetwork()
}

Q3. いつコルーチンを使用すべきですか?

A: このような場合に使用してください!

// ✅ ネットワークリクエスト
suspend fun loadData() = api.fetchData()

// ✅ データベース操作
suspend fun saveUser(user: User) = db.insert(user)

// ✅ ファイルI/O
suspend fun readFile(path: String) = withContext(Dispatchers.IO) {
File(path).readText()
}

// ✅ 長い計算タスク
suspend fun heavyComputation() = withContext(Dispatchers.Default) {
// 複雑な計算
}

🎬 まとめ

コルーチンで非同期プログラミングを簡単に!

要点まとめ:
✅ コールバック地獄からの脱出
✅ 軽量な同時実行
✅ 直感的なコード
✅ 構造化されたライフサイクル
✅ スレッドよりはるかに効率的

次のステップ: コルーチン基礎で実際にコルーチンを使ってみましょう!