1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Kotlin】Kotlin の `CoroutineScope` を徹底解説

Posted at

はじめに

〜コルーチンの「生存範囲」を制御するスコープの仕組み〜


概要(Overview)

Kotlin のコルーチンを使うとき、必ずといっていいほど登場するのが CoroutineScope
これは一言で言えば:

「コルーチン(Coroutine)のライフサイクルを管理する器」

CoroutineScope を使うことで、

  • どの範囲でコルーチンが生きるか
  • どのタイミングでキャンセルするか
  • どのスレッド上で動かすか

…といった「管理」を安全に行えるようになります。


コルーチンの基本構造

まず、コルーチンを動かす最小構成を見てみましょう。

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        delay(1000)
        println("Hello from coroutine!")
    }
}

ここで runBlocking が「スコープ(CoroutineScope)」を作っています。
そして launch { ... } が「このスコープ内で実行されるコルーチン」です。


CoroutineScope の役割

CoroutineScope は、コルーチンの構造的な親子関係(Structured Concurrency) を保つために存在します。

要素 役割
CoroutineScope コルーチンのライフサイクルを管理するコンテナ
Job コルーチン自体の実行単位(キャンセル・完了を管理)
Dispatcher コルーチンが動作するスレッドプール(IO / Main / Defaultなど)

イメージ図

スコープが消えると、すべての子コルーチンがキャンセルされます。
これが「構造化並行性(Structured Concurrency)」の基本思想です。


CoroutineScope の作り方

1. runBlocking(メイン関数でよく使う)

fun main() = runBlocking {
    println("Start")
    launch {
        delay(500)
        println("Work done")
    }
    println("End")
}

runBlocking現在のスレッドをブロックしてコルーチンを実行。
通常はメイン関数やテストで使用。


2. CoroutineScope()(明示的に作る)

val scope = CoroutineScope(Dispatchers.Default)

scope.launch {
    delay(1000)
    println("Hello from my scope!")
}

Dispatchers.Default 上でコルーチンを起動。
アプリ全体やサービス単位で独自スコープを持ちたいときに使用。


3. MainScope()(UIアプリなど)

class MyViewModel : ViewModel() {
    private val scope = MainScope()

    fun loadData() {
        scope.launch {
            delay(1000)
            println("Data loaded")
        }
    }

    override fun onCleared() {
        scope.cancel() // 🔥 スコープ終了時にキャンセル
    }
}

Android / Compose / ViewModel でよく使うパターン。
UI破棄時に自動キャンセルが可能。


CoroutineScope のキャンセル

CoroutineScope には「キャンセル連鎖」があります。
親スコープがキャンセルされると、子コルーチンもすべて停止します。

val scope = CoroutineScope(Dispatchers.Default)

val job = scope.launch {
    repeat(5) {
        println("Working $it ...")
        delay(500)
    }
}

Thread.sleep(1000)
scope.cancel() // 🔥 親スコープをキャンセル

出力:

Working 0 ...
Working 1 ...
(キャンセルで終了)

cancel() すればすべての子がまとめて停止。
メモリリーク防止・安全な終了処理が可能。


スコープとコンテキスト(Context)

CoroutineScopeCoroutineContext を持っています。
コンテキストには主に以下が含まれます:

要素 説明
Job Job コルーチンのキャンセル・完了管理
Dispatcher CoroutineDispatcher 実行スレッド指定(IO, Default, Main)
ExceptionHandler CoroutineExceptionHandler 例外ハンドリング

例:複数設定をまとめてスコープ生成

val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

複数子コルーチンを独立して実行するための SupervisorJob() も利用可能。


構造化並行性(Structured Concurrency)

CoroutineScope があるおかげで、
「コルーチンの生存範囲」「エラー伝播」「キャンセル制御」がすべて自動化されます。

スコープがないコード(危険)

GlobalScope.launch { delay(1000); println("Leaked!") }

GlobalScope はアプリ終了まで生き続ける(リークリスクあり)

スコープを使った構造化

suspend fun doWork() = coroutineScope {
    launch { delay(1000); println("A done") }
    launch { delay(500); println("B done") }
}

coroutineScope {} 内で起動した子は、すべて完了するまで待機。
安全・明確な並行構造。


コード例:親子スコープの違い

import kotlinx.coroutines.*

fun main() = runBlocking {
    println("Start")
    launch {
        delay(1000)
        println("Child coroutine finished")
    }
    println("End of runBlocking")
}

runBlocking が終了するとき、 中の launch が完了するまで自動的に待機します。
これが「構造化されたスコープ管理」です。


よく使うスコープ一覧

スコープ名 用途 スレッド動作
runBlocking メイン・テストコード 呼び出しスレッドをブロック
GlobalScope 永続的なバックグラウンド処理(非推奨) アプリ終了まで生存
CoroutineScope() 任意のスコープ定義 任意のDispatcher
MainScope() UI系クラス(ViewModel, Activity) Mainスレッド
coroutineScope {} suspend関数内で一時的に使う 呼び出しスレッドと連動

まとめ

概念 説明
CoroutineScope コルーチンのライフサイクルを管理する枠組み
launch / async スコープ内でコルーチンを起動
cancel() スコープ全体をキャンセル可能
Dispatcher 実行スレッドを指定(Main / IO / Default)
構造化並行性 親スコープが子コルーチンを安全に管理する仕組み

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?