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

More than 5 years have passed since last update.

Kotlin 1.3のCoroutineのキャンセル⑤(キャンセルさせないブロック)

Posted at

検証環境

この記事の内容は、以下の環境で検証しました。

  • Intellij IDEA ULTIMATE 2018.2
  • Kotlin 1.3.0
  • Gradle Projectで作成
  • GradleファイルはKotlinで記述(KotlinでDSL)

準備

詳細は下記の準備を参照してください。
https://qiita.com/naoi/items/8abf2cddfc2cb3802daa

Run non-cancellable block

前回に引き続き、公式サイトを読み解いていきます。

今回のタイトルは「キャンセルさせない」です。これまではキャンセルさせることに注力していましたが、今回はキャンセルさせないってことでしょうか。

早速、読んでいきます。

Any attempt to use a suspending function in the finally block of the previous example causes CancellationException, because the coroutine running this code is cancelled. Usually, this is not a problem, since all well-behaving closing operations (closing a file, cancelling a job, or closing any kind of a communication channel) are usually non-blocking and do not involve any suspending functions. However, in the rare case when you need to suspend in the cancelled coroutine you can wrap the corresponding code in withContext(NonCancellable) {...} using withContext function and NonCancellable context as the following example shows:

意訳込みですが、訳してみると以下のようになります。

サスペンド関数実行中にキャンセルするとCancellationExceptionが発生します。これはfinally句でも同じです。finally句でよく記述するリソースの開放などは非ブロッキング関数なので、例外は発生しません。しかし、たまにですが、finally句でサスペンド関数を呼び出すことがあります。そのようなケースでは、例外が発生し、finally句の処理が途中で中断されてしまいます。回避する方法として、withContext関数とNonCancellableを使用することで一時的に非ブロッキングにできます。詳細は、サンプルコードをみてください。

確かに、finally句でサスペンド関数を呼ぶとキャンセルされそうですね。それも回避できるのは嬉しいです。早速、サンプルコードを確認してみます。

import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch {
        try {
            repeat(1000) { i ->
                println("I'm sleeping $i ...")
                delay(500L)
            }
        } finally {
            withContext(NonCancellable) {
                println("I'm running finally")
                delay(1000L)
                println("And I've just delayed for 1 sec because I'm non-cancellable")
            }
        }
    }
    delay(1300L) // delay a bit
    println("main: I'm tired of waiting!")
    job.cancelAndJoin() // cancels the job and waits for its completion
    println("main: Now I can quit.")
}

finally句にwithContext関数が呼び出されていますね。
あと、注目する点としては、withContext(NonCancellable) 関数内でdelay関数が呼び出されています。
delay関数はサスペンド関数なので、通常だったらキャンセルされて、「"And I've just delayed for 1 sec because I'm non-cancellable"」は表示されない。しかし、今回は表示されるはず!
実行してみました。

I'm sleeping 0 ...
I'm sleeping 1 ...
I'm sleeping 2 ...
main: I'm tired of waiting!
I'm running finally
And I've just delayed for 1 sec because I'm non-cancellable
main: Now I can quit.

逆に、withContext(NonCancellable) 関数で囲まないとどうなるのか気になりますね。
サンプルコードを以下のように修正してみました。

import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch {
        try {
            repeat(1000) { i ->
                println("I'm sleeping $i ...")
                delay(500L)
            }
        } finally {
            println("I'm running finally")
            delay(1000L)
            println("And I've just delayed for 1 sec because I'm non-cancellable")

        }
    }
    delay(1300L) // delay a bit
    println("main: I'm tired of waiting!")
    job.cancelAndJoin() // cancels the job and waits for its completion
    println("main: Now I can quit.")
}

実行結果を確認してみると、以下のようになりました。

I'm sleeping 0 ...
I'm sleeping 1 ...
I'm sleeping 2 ...
main: I'm tired of waiting!
I'm running finally
main: Now I can quit.

確かに、「And I've just delayed for 1 sec because I'm non-cancellable」が表示されていない。
delay関数がキャンセルされたと考えられます。

まとめ

このブロックで理解できたことは以下のことだと思います。

  • サスペンド関数で キャンセルを一時的に無効にすることができる
  • withContext(NonCancellable) 内は一時的にキャンセルを無効にできる
3
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
3
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?