coroutine cancel 에 대해서 자세히 알아보자.
job.cancelAndJoin()
job 에 대해서 cancelAndJoin() 이라는 함수가 제공된다. 왜? 나는 join은 invoking coroutine 의 작업을 하위 coroutine 의 작업이 '완료'될때 까지 기다리는 것이라고 알고 있었지만 이 '완료'의 범위 안에는 '작업 중지'도 포함이 된다. 즉, invoking coroutine 에게 "내가 이 하위 coroutine 의 작업을 취소할껀데, 작업 중지가 완료될때까지 기다려 달라" 고 말하는 것과 같은 것이다.
Dispacher.Default 에 대한 cancel
cancel 은 Coroutine Job 의 작업을 취소시키는 것이라고 알고 있다. 그런데, Dispacher.Default 에서 computation 작업중에는 cancel 이 지연되는 현상이 나타난다.
fun main() {
runBlocking {
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.")
}
}
이 코드의 실행결과는 아래와 같다.
main: I'm tired of waiting! 다음에 바로 main: Now I can quit. 아 나와야하는게 맞는것 같은데, loop 문이 멈추지 않고 다 돌아가버리고 말았다.
job: I'm sleeping 0 ...
job: I'm sleeping 1 ...
job: I'm sleeping 2 ...
main: I'm tired of waiting!
job: I'm sleeping 3 ...
job: I'm sleeping 4 ...
main: Now I can quit.
이러한 문제는 Dispacher를 다르게 적용하면 해결될 수 있지만, 분명 Dispacher.Default 를 써야하는 상황이 있기 때문에 본질적인 해결방법이 될 수 없다.
이러한 문제는 해당 coroutine scope 가 여전히 유효한지 체크함으로써 해결될 수 있고, 내부 loop 문을
while (isActive) 로 바꾸면 해결된다.