잡히지 않은 예외가 발생하면 프로그램이 종료되는 것처럼 코루틴도 잡히지 않은 예외가 발생했을 때 종료된다. 스레드 또한 같은 경우에 종료된다. 차이가 있다면 코루틴 빌더는 부모도 종료시키며, 취소된 부모는 자식들 모두를 취소시킨다는 점이다.
아래 예시는 코루틴이 예외를 받았을 때 자기 자신을 취소하고 예외를 부모로 전파한다(launch). 부모는 자기 자신과 자식들 모두를 취소하고 예외를 부모에게 전파한다(runBlocking). runBlocking은 부모가 없는 코루틴이기 때문에 프로그램을 종료시킨다(runBlocking은 예외를 다시 던진다).
fun main(): Unit = runBlocking {
launch {
launch {
delay(1000)
throw Error("Some error")
}
launch {
delay(2000)
println("Will not be printed")
}
launch {
delay(500)
println("Will be printed")
}
}
launch {
delay(2000)
println("Will not be printed")
}
}
Will be printed
Exception in thread "main" java.lang.Error: Some error
launch 코루틴을 더하는 건 아무것도 바꾸지 못한다. 예외는 자식에서 부모로 전파되며, 부모가 취소되면 자식도 취소되기 때문에 쌍방으로 전파된다. 예외 전파가 정지되지 않으면 계통 구조상 모든 코루틴이 취소된다.
코루틴이 종료되기 전에 예외를 잡는 건 도움이 되지만, 코루틴 간의 상호작용은 잡을 통해서 일어나기 때문에, 코루틴 빌더 내부에서 새로운 코루틴 빌더를 try-catch 문을 통해 래핑하는 건 전혀 도움이 되지 않는다.
fun main(): Unit = runBlocking {
try {
launch {
delay(1000)
throw Error("Some error")
}
} catch (e: Throwable) {
println("Will not be printed")
}
launch {
delay(2000)
println("Will not be printed")
}
}
Exception in thread "main" java.lang.Error: Some error
코루틴 종료를 멈추는 가장 중요한 방법은 SupervisorJob을 사용하는 것이다. SupervisorJob을 사용하면 자식에게 발생한 모든 예외를 무시할 수 있다.
일반적으로 SupervisorJob은 다수의 코루틴을 시작하는 스코프로 사용된다.
fun main(): Unit = runBlocking {
val scope = CoroutineScope(SupervisorJob())
scope.launch {
delay(1000)
throw Error("Some error")
}
scope.launch {
delay(2000)
println("Will be printed")
}
delay(3000)
}
Exception in thread "DefaultDispatcher-worker-1" java.lang.Error: Some error...
Will be printed
흔한 실수 중 하나는 SupervisorJob을 다음 코드처럼 부모 코루틴의 인자로 사용하는 것이다. 1에서 정의된 launch가 SupervisorJob을 인자로 받는데, 이럴 경우 SupervisorJob은 단 하나의 자식만 가지기 때문에 예외를 처리하는 데 아무런 도움이 되지 않는다. 따라서 SupervisorJob을 Job 대신 사용하더라도 아무 도움이 되지 않는다.(두 경우 모두 runBlocking의 잡을 사용하지 않기 때문에 예외는 runBlocking으로 전파되지 않는다).
fun main(): Unit = runBlocking {
// 자식 코루틴 하나가 있고, 부모 코루틴이 없는 잡은 일반 잡과 동일하게 작동한다.
launch(SupervisorJob()) { // 1
launch {
delay(1000)
throw Error("Some error")
}
launch {
delay(2000)
println("Will not be printed")
}
}
delay(3000)
}
Exception in thread "main" java.lang.Error: Some error