Cancellation of coroutines
  • Cancelling phi cancels its subcoroutine
  • Cancelled subcoroutines do not affect the remaining sibling coroutines
  • The coroutine handles the cancellation by running a special CancellationException
  • All pending functions (withContext, delay, etc.) in Kotlinx.coroutines can be cancelled
fun testCancellationException(a)= runBlocking {
    val job1 = GlobalScope.launch {
        try {
            delay(1000)
            println("job 1")}catch(e: Exception) {
            e.printStackTrace()
        }
    }
    delay(100)
    job1.cancel()
    job1.join()
}
Copy the code
kotlinx.coroutines.JobCancellationException: 
StandaloneCoroutine was cancelled; job=StandaloneCoroutine{Cancelling}@ac54805
Copy the code

Job1 is delayed by 100 milliseconds. Job1 has entered suspend cancel and will throw an exception that has been silently handled. If you want to see the exception, you can add try cash to the coroutine and see 2. Why would I say join in this code? If I didn’t say join, I wouldn’t print out the contents of GlobalScope because GlobalScope has its own scope

Cpu-intensive tasks are cancelled
  • IsActive is an extended property that can be used in the CoroutineScope to check if a Job isActive.
  • EnsureActive (), which immediately throws an exception if the job is inactive
  • The yield function checks the state of the host coroutine and throws a CancellationException if it has been cancelled. In addition, it tries to delegate execution rights to the thread, giving other coroutines the opportunity to execute
fun testCancelCPUTaskByIsActive(a)= runBlocking {
    val startTime = System.currentTimeMillis()
    val job = launch(Dispatchers.Default) {
        var i = 0
        var nexPrintTime = startTime
        while(i<5 && isActive){
             // ensureActive()
             // yield()
            if(System.currentTimeMillis() >= nexPrintTime){
                println("job : I'm sleeping ${i++}...")
                nexPrintTime+=1000
            }
        }
    }
    delay(500)
    println("main: I'm tired of waiting!")
    job.cancel()
    println("Main: Now I can quit.")}Copy the code

Look at the code above

  1. If the isActive judgment is not added to the while condition, the while loop will continue after job.cancel() is executed. The coroutine will provide some protection, because some temporary calculation data will disappear if the condition is cancelled directly, so add isActive judgment and cancel and exit directly

  2. With ensureActive(), the internal implementation still uses isActive and throws an exception if it is not active

CancellationException

  1. Yield () grants execution, and to prevent yourself from grabbing all the resources, ask if anything else needs to be executed before each call
Side effects of coroutine cancellation
  • Release resources in finally
  • Use function: this function can only be used by objects with Closeable, the close method is automatically called at the end of the program, suitable for file objects
fun testReleaseResources(a)= runBlocking {
    val job = launch {
        try {
            repeat(1000){ index->
                println("job : I'm sleeping $index...")
                delay(500)}}finally {
            println("job: I'm running finally")
        }
    }
    delay(1300)
    println("main: I'm tired of waiting!")
    job.cancelAndJoin()
    println("Main: Now I can quit.")}Copy the code
fun testUseFunction(a) = runBlocking {
    val br = BufferedReader(FileReader("build.gradle.kts"))
    br.use {
        var line : String?
        while(true){ line = it.readLine() ? :break
            println(line)
        }
    }
}
Copy the code
Tasks that cannot be cancelled
  • Coroutines in the canceled state cannot be suspended (run code that cannot be canceled). When the coroutine is canceled, the suspended function needs to be called. We need to place the code for the cleanup task in the NonCancellable CoroutineContext
  • This suspends running code and holds the canceled state of the coroutine until task processing is complete
fun testReleaseResources(a)= runBlocking {
    val job = launch {
        try {
            repeat(1000){ index->
                println("job : I'm sleeping $index...")
                delay(500)}}finally {
            println("job: I'm running finally")
        }
    }
    delay(1300)
    println("main: I'm tired of waiting!")
    job.cancelAndJoin()
    println("Main: Now I can quit.")}Copy the code

In this code, we need NonCancellable if we call delay(1000) in finally and it will not execute

finally {
   withContext(NonCancellable){
       println("job: I'm running finally")
       delay(1000)
       println("job: And I've just delayed for 1 sec because I'm non-cancellable")
   }
}
Copy the code
Timeout task
  • A lot of times the reason to take out a coroutine is because it might time out
  • WithTimeoutOrNull Executes a timeout operation by returning NULL instead of throwing an exception
fun testDealWithTimeout(a) = runBlocking {
    val result = withTimeoutOrNull(1300){
        repeat(1000){ index->
            println("job : I'm sleeping $index...")
            delay(500)}"Done"
    }
    println("Result is $result") // It prints Done if it is finished and null if it is not
}
Copy the code