Cancellation and Timeouts


Kotlin公式文書
Cancelling coroutine execution
  • coroutineは不要です.ジョブをコミットしてからキャンセルしてください.
  • val job = launch {
        repeat(1000) { i ->
            println("job: I'm sleeping $i ...")
            delay(500L)
        }
    }
    delay(1300L) // delay a bit
    println("main: I'm tired of waiting!")
    job.cancel() // cancels the job
    job.join() // waits for job's completion 
    println("main: Now I can quit.")
  • job.cancelAndJoin()job.cancel()およびjob.join()は同時に完了することができる.
  • Cancellation is cooperative
  • すべてのsuspend関数はキャンセル可能であり、CancellationExceptionを放出します.
  • ただし、coroutineはworking in a computationであり、キャンセルチェックを行わないとキャンセルされません.
  • val startTime = System.currentTimeMillis()
    val job = launch(Dispatchers.Default) {
        var nextPrintTime = startTime
        var i = 0
        while (i < 5) { // computation loop, just wastes CPU
            // print a message twice a second
            if (System.currentTimeMillis() >= nextPrintTime) {
                println("job: I'm sleeping ${i++} ...")
                nextPrintTime += 500L
            }
        }
    }
    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.")
    Making computation code cancellable
  • 上記の問題を解決するために、まず、ループ内の各ループにyield()を呼び出す方法がある.
  • yield()coroutineがキャンセルまたは完了した場合、CancellationExceptionを放出します.
  • ループ条件文には、isActiveを確認する方法があります.
  • closing resources with finally
  • coroutine cancelはCancellationExceptionで行います.
  • try/finally定義を使用してcancelを完了したタスクを以下に示します.(もちろん、これはcatchにもつながります.)
  • functionはまた、リソースusefunctionを閉じるために使用することができる.(詳細)
  • val job = launch {
        try {
            repeat(1000) { i ->
                println("job: I'm sleeping $i ...")
                delay(500L)
            }
        } finally {
            println("job: I'm running finally")
        }
    }
    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.")
    Run non-cancellable block
  • 特別な場合、キャンセルされたcoroutineでsuspend関数を使用する必要がある場合があります.
  • この場合NonCancellablecoroutine contextを使用します.
  • withContext(NonCancellable) {
        println("job: I'm running finally")
        delay(1000L)
        println("job: And I've just delayed for 1 sec because I'm non-cancellable")
    }
    Timeout
  • cancel()を使用する代わりにwithTimeout()を使用してTimeoutCancellationExceptionタイムアウトをトリガーすることもできる.
  • try/catchは、終了動作の処理に用いることができる.
  • またはwithTimeoutNull()を使用して、exceptionの代わりにnullを使用することもできます.
  • val result = withTimeoutOrNull(1300L) {
        repeat(1000) { i ->
            println("I'm sleeping $i ...")
            delay(500L)
        }
        "Done" // will get cancelled before it produces this result
    }
    println("Result is $result")
    Asynchronous timeout and resources
  • withTimeout時timeout 이벤트およびblock 내부의 코드非同期運転.
  • したがって、単独で処理しなければブロック内でのコードの実行は保証されない.
  • finallyは、リソースの参照を提供し、close()が以下に示すことを保証します.
  • runBlocking {
        repeat(100_000) { // Launch 100K coroutines
            launch { 
                var resource: Resource? = null // Not acquired yet
                try {
                    withTimeout(60) { // Timeout of 60 ms
                        delay(50) // Delay for 50 ms
                        resource = Resource() // Store a resource to the variable if acquired      
                    }
                    // We can do something else with the resource here
                } finally {  
                    resource?.close() // Release the resource if it was acquired
                }
            }
        }
    }
    // Outside of runBlocking all coroutines have completed
    println(acquired) // Print the number of resources still acquired