3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

runInterruptible: Coroutine上で時間のかかるコード実行を適切にキャンセルする

Last updated at Posted at 2024-10-04

みなさんはKotlin Coroutines上で時間のかかる処理を行う際、処理のキャンセルについて意識されてますでしょうか?

この記事では、Coroutine上で動かす時間のかかるコード実行を適切にキャンセルするにはwithContextではなくrunInterruptibleを利用するべきであるという例を紹介します。

時間のかかるコード実行をキャンセルさせる例として以下のようなコードを用意しました。

runBlocking {
    val heavyWork = fun(): String {
        println("Start heavy work")
        repeat(10) {
            Thread.sleep(1000)
            println("Progress ${it + 1}")
        }
        return "Success"
    }
    val result = withTimeoutOrNull(5000) {
        withContext(Dispatchers.IO) {
            heavyWork()
        }
    }
    println("Result: $result")
}

時間のかかる処理のシミュレートとしてheavyWorkを定義しました。このメソッドの完了に10秒掛かります。
厳密にメソッドを10秒間ブロックするため、delay等のCoroutineの機能を利用せずに非Coroutineで実装していることが時間のかかる処理を再現する上でのポイントです。

このメソッドをCoroutine上で実行しますが、withTimeoutOrNullによって5秒のタイムアウトを設定しているため、実際にはProgress 5以降は出力されないことを意図しています。

しかし、出力結果は以下の通りになります。

Start heavy work
Progress 1
Progress 2
Progress 3
Progress 4
Progress 5
Progress 6
Progress 7
Progress 8
Progress 9
Progress 10
Result: null

なぜタイムアウト時点で処理が停止せずProgress 10まで出力されたのかというと、非Coroutineなメソッドの実行中は途中でCancellationExceptionをthrowするタイミングが存在しないため、呼び出し側のCoroutineがキャンセルされた後も意図せず処理が続行されてしまいます。

そこでrunInterruptibleを利用します。

時間の掛かる処理の実行時に使っていたwithContextrunInterruptibleに入れ替えることによって、この問題は解消します。

runBlocking {
    val heavyWork = fun(): String {
        println("Start heavy work")
        repeat(10) {
            Thread.sleep(1000)
            println("Progress ${it + 1}")
        }
        return "Success"
    }
    val result = withTimeoutOrNull(5000) {
        runInterruptible(Dispatchers.IO) {
            heavyWork()
        }
    }
    println("Result: $result")
}

このコードの出力結果は以下の通りになり、タイムアウト後ただちに処理が中断されていることが分かります。

Start heavy work
Progress 1
Progress 2
Progress 3
Progress 4
Result: null

以上、ブロックの実行が途中であってもCoroutineのキャンセル時にCancellationExceptionをthrowし、適切に処理をキャンセルできるrunInterruptibleの紹介でした。

3
3
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
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?