Exception Handling in Kotlin Coroutine
kotlin coroutine
- Exceptions according to Coroutine Scope
- How to Exception Handling in Coroutine ?
- How Coroutines Distinguish Exceptions
Exceptions according to Coroutine Scope
별도의 Root CoroutineScope 를 가지는 Coroutine 에서 예외를 throw 하는 경우:
fun main(): Unit = runBlocking { // Root CoroutineScope 1
printWithThread("START")
// CoroutineScope 를 사용하면 runBlocking 과의 별개의 root scope 를 갖는다.
CoroutineScope(Dispatchers.IO).launch { // Root CoroutineScope 2
printWithThread("IO Thread Start")
throw IllegalArgumentException()
printWithThread("IO Thread End")
}
delay(1000L)
printWithThread("END")
}
위 결과는 실제로 예외가 출력되며 종료된다. launch 대신 async 를 사용하는 경우에는 jog 타입이 Deferred 이므로 await() 을 호출해야 예외가 발생 한다.
부모(Root) CoroutineScope 를 가지는 Coroutine 에서 예외를 throw 하는 경우:
fun main(): Unit = runBlocking { // Root CoroutineScope
printWithThread("START")
launch { // CoroutineScope
printWithThread("IO Thread Start")
throw IllegalArgumentException()
printWithThread("IO Thread End")
}
delay(1000L)
printWithThread("END")
}
자식 코루틴에서 발생한 예외는 부모 코루틴에게 전파(propagation) 된다. 따라서, launch 든, async 든 예외를 내뱉고 종료된다. 이때 async 는 await 을 호출하지 않아도 예외가 발생한다.
async 를 사용하는 경우 SupervisorJob() 을 인자로 주게되면 await 을 사용해야 예외가 발생한다.
async(SupervisorJob()) {
throw IllegalArgumentException()
}
How to Exception Handling in Coroutine ?
try-catch:
fun main(): Unit = runBlocking {
launch {
try {
throw IllegalArgumentException()
} catch (e: Exception) {
printWithThread("Catching Exception")
}
}
}
- Exception 발생 이후 로깅, 예외 메시지 전송에 유용하다.
- launch 에만 적용 가능하며 부모 코루틴이 있으면 동작하지 않는다.
@OptIn(DelicateCoroutinesApi::class)
fun main() = runBlocking {
val handler = CoroutineExceptionHandler { _, exception ->
println("CoroutineExceptionHandler got $exception")
}
val job = GlobalScope.launch(handler) {
val inner = launch { // all this stack of coroutines will get cancelled
launch {
launch {
throw IOException() // the original exception
}
}
}
try {
inner.join()
} catch (e: CancellationException) {
println("Rethrowing CancellationException with original cause")
throw e // cancellation exception is rethrown, yet the original IOException gets to the handler
}
}
job.join()
}
Outputs:
Rethrowing CancellationException with original cause
CoroutineExceptionHandler got java.io.IOException
How Coroutines Distinguish Exceptions
- CancellationException
- 코루틴을 취소로 간주한다. 예외를 부모로 전파하지 않는다.
- Other Exceptions
- 예외로 간주한다. 부모로 전파한다.
State Machine:
- NEW -> ACTIVE -> CANCELLING -> CANCELLED
여기서 CANCELLING
을 두는 이유는 ChatGPT 피셜 다음과 같다고 한다.
- Graceful Shutdown: 코루틴은 일부 작업을 수행하고 있는 중에 취소 요청을 받을 수 있습니다. CANCELLING 상태를 사용하면 이러한 작업을 완료한 후에 코루틴을 취소할 수 있으며, 그동안 다른 리소스를 정리하고 정리 작업을 수행할 수 있습니다.
- Cleanup and Resource Release: 코루틴이 CANCELLING 상태에 있을 때, 리소스를 해제하거나 정리할 수 있습니다. 이는 예를 들어 열린 파일이나 네트워크 연결을 닫는 등의 작업을 수행할 때 유용합니다.
- Progress Monitoring: CANCELLING 상태를 사용하여 얼마나 많은 작업이 완료되었는지 또는 얼마나 많은 작업이 취소되었는지를 추적하고 모니터링할 수 있습니다.