coroutine Cancellation

참치돌고래·2022년 12월 1일
0
post-custom-banner

Basic cancellation

job 인터페이스는 cancel 메소드를 가진다.
1. 첫번째 suspension point에서 job을 끝낸다.
2. 만약 job이 children이 있다면, 그들 또한 취소한다.
3. job이 취소되면 어떠한 새로운 coroutine들도 해당 job을 부모로 사용할 수 없다.
4. 위의 과정이 끝나면 Cancelled 상태가 된다.

suspend fun main() = coroutineScope {
    val job = launch {
        repeat(1000) { i ->
            delay(200)
            println("Printing $i")
        }
    }
    delay(1100)
    job.cancel()
    job.join()
    println("Cancelled successfully")
}

output

Printing 0
Printing 1
Printing 2
Printing 3
Printing 4
Cancelled successfully

부모 job이 끝나자 repeat(1_000)을 돌아야하는 자식 job이 실행 도중 취소되었다.
만약 join 메소드를 사용하지 않으면 race conditions 가 될 수 있다.
join 메소드를 사용해서 coroutine이 cancellation을 끝낼 때까지 기다리는 것이 좋다.
cancelAndJoin()을 통해 한번에 cancel과 join을 실행할 수 있다.

How does cancellation work?

job이 취소되면 상태는 Cancelling 상태로 변한다. 그리고 첫번째 suspension point에서
CancellationException이 던져진다. 이러한 예외는 try-catch문으로 잡은뒤 다시 새로 던지는 것이 좋다.


suspend fun main() = coroutineScope {
    val job = launch {
        try {
            repeat(1000) { i ->
                delay(200)
                println("Printing $i")
            }
        }catch (e : CancellationException) {
            println(e)
            throw e
        }
    }
    delay(1100)
    job.cancel()
    job.join()
    
    println("Cancelled successfully")
}

예외를 던지는 것 뿐 아니라, finally 구문을 통해 coroutine이 종료된 후에도,
항상 실행되는 suspeding call을 할 수 있다.


suspend fun main() = coroutineScope {
    val job = launch {
        try {
            repeat(1000) { i ->
                delay(200)
                println("Printing $i")
            }
        }finally {
            println("Finally")
            withContext(NonCancellable){
                delay(1000L)
                println("Cleanup Done")
            }
        }
    }
    delay(1100)
    job.cancel()
    job.join()
    println("Cancelled successfully")
}

output

Printing 0
Printing 1
Printing 2
Printing 3
Printing 4
Finally
Cleanup Done
Cancelled successfully

invokeOnCompletion

invokeOnCompletion을 통해 free resource 명시
job이 종료될 때, 사용되는 handler 로 세팅할 수 있다.


suspend fun main() = coroutineScope {
    val job = launch {
        delay(1000)
    }
    job.invokeOnCompletion { exception : Throwable? ->
        println("Finished")
    }
    delay(400)
    job.cancelAndJoin()
}

output


Finished
  • 만약 exception이 던져지지 않으면 handler의 parameter는 null이다.
  • 만약 coroutine이 취소되면 CancellationException이다.

만약 invokeOnCompletion이 호출되기 전에 job이 완료되면, handler는 즉시, invoke...를 호출한다.

profile
안녕하세요
post-custom-banner

0개의 댓글