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을 실행할 수 있다.
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을 통해 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
CancellationException
이다.만약 invokeOnCompletion
이 호출되기 전에 job이 완료되면, handler는 즉시, invoke...
를 호출한다.