인프런 커뮤니티 질문&답변

윤성식님의 프로필 이미지
윤성식

작성한 질문수

2시간으로 끝내는 코루틴

자식1, 2와 부모코루틴의 관계

작성

·

147

·

수정됨

1

본 강의를 모두 수강하였습니다! 코루틴에서 헷갈렸던 개념들을 알 수 있어서 좋았습니다!

본 코루틴 강의에서 자식과 부모 관계의 에러가 발생했을 때 에러 핸들링 하는 경우는 자식이 하나만 존재했을 때의 예시밖에 없어서 직접 2개를 가지고 실험을 해보았습니다!

 

먼저, 자식1이 취소가 됐을 때에는 취소예외가 발생했기 때문에 부모로 전파되지 않고, 그러므로 다른 자식2도 영향을 받지 않는다 라고 이해를 하고 있습니다!

하지만 자식1에서 취소가 아닌 예외가 발생했을 경우 부모로 전파되는 것으로 알고있고, 이 때 적절한 조치가 되지 않는다면 자식2까지 취소되는 것으로 알고있습니다!

따라서 다음과 같이 CoroutineExceptionHandler를 적용해보았습니다.

fun main() = runBlocking {
    val handler = CoroutineExceptionHandler { _, exception ->
        println("Caught exception: $exception")
    }
    val parentJob = CoroutineScope(Dispatchers.Default).launch(handler) {
        val job1 = launch {
            println("Job 1 is running")
            throw RuntimeException("Error in Job 1")
        }
        val job2 = launch {
            println("Job 2 is running")
            delay(1000)
            println("Job 2 is completed")
        }
        job1.join()
        job2.join()
    }
    parentJob.join()
    println("Parent job is completed")
}

Job 1 is running

Caught exception: java.lang.RuntimeException: Error in Job 1

Parent job is completed
결과는 자식1만 실행이되고, 거기서 Exception을 던졌는데, Job2는 취소되는것으로 보입니다..!
질문 1 ) 다음과 같이 try catch 로 전환하면 자식2의 취소가 발생하지 않는데, CoroutineExceptionHandler로는 자식2의 취소를 막을수는 없는 것일까요?

val job1 = launch {
    println("Job 1 is running")
    try {
        throw RuntimeException("Error in Job 1")
    } catch (e: RuntimeException) {
        println("RuntimeException")
    }
}

질문 2) 강의내용에서는 SupervisorJob() 을 사용하면 부모 코루틴으로 예외가 전파되지 않는다고 해주셨는데, 다음 결과에서는 부모코루틴에 해당되는 CoroutineExceptionHandler 가 실행되는 것으로 보입니다. 하지만, 질문1에서 걱정하는 자식2의 취소로 이어지지 않고 있습니다. 이는 부모코루틴으로 예외가 전파되는 상황일까요?? 만일 전파되는 상황이라면 왜 질문1과는 다르게 자식2의 취소로 이어지지 않는 것일까요?

fun main() = runBlocking {
    val handler = CoroutineExceptionHandler { _, exception ->
        println("Caught exception: $exception")
    }
    val parentJob = CoroutineScope(Dispatchers.Default).launch(handler) {
        val job1 = launch(SupervisorJob()) {
            println("Job 1 is running")
            throw RuntimeException("Error in Job 1")
        }
        val job2 = launch {
            println("Job 2 is running")
            delay(1000)
            println("Job 2 is completed")
        }
        job1.join()
        job2.join()
    }
    parentJob.join()
    println("Parent job is completed")
}

Job 1 is running

Job 2 is running

Caught exception: java.lang.RuntimeException: Error in Job 1

Job 2 is completed

Parent job is completed

자바 -> 코틀린 강의부터 시작해서 코루틴 강의까지 너무 감사하게 잘 보고있습니다! 감사드립니다 🙂

답변 1

1

최태현님의 프로필 이미지
최태현
지식공유자

안녕하세요 성식님! 🙂 좋은 질문 주셔서 정말 감사드립니다! 🙏

결론부터 말씀드리면 CoroutineExceptionHandler 라는 이름이 마치 예외를 막아줄 것 처럼 느껴지지만, CoroutineExceptionHandler 은 예외에 대한 로직을 처리할 뿐 예외를 막지는 않습니다!

제가 코루틴에서 예외를 처리하는 방법을 소개해드리며, try - catch와 CoroutineExceptionHandler 을 같이 말씀드려서 조금 더 헷갈리셨을 수 있겠네요! 😭 (다른 점을 강조해둘 걸 그랬습니다!)

 

실제로 공식 문서의 설명을 보면

It is similar to Thread.uncaughtExceptionHandler. You cannot recover from the exception in the CoroutineExceptionHandler.

이라는 설명이 있는데요, CoroutineExceptionHandlerThread.uncaughtExceptionHandler 와 비슷하고 핸들러 안에서 예외를 recover (=> 예외를 먹다, 막다 정도로 해석하면 될 것 같아요!) 할 수 없다고 되어 있습니다. 즉, try - catch는 특정한 예외를 막아 코루틴 안에서 예외가 발생하지 않은 것으로 간주하는 반면, CoroutineExceptionHandler 는 예외를 발생한 것으로 간주하되, 예외에 관한 로직을 핸들링 할 수 있는 방법인 셈이죠!

 

이 사실을 명확히 하고 질문 1, 2번을 보면 조금 더 명확해집니다.

 

[질문 1. 다음과 같이 try catch 로 전환하면 자식2의 취소가 발생하지 않는데, CoroutineExceptionHandler로는 자식2의 취소를 막을수는 없는 것일까요?]

CoroutineExceptionHandler로는 자식의 취소를 막을 수 없고 따라서 부모로 예외가 전파되어 다른 sibling coroutine도 취소가 이루어집니다!

 

[질문 2. 이는 부모코루틴으로 예외가 전파되는 상황일까요?? 만일 전파되는 상황이라면 왜 질문1과는 다르게 자식2의 취소로 이어지지 않는 것일까요?]

우선 예외가 발생했고, 해당 예외는 CoroutineExceptionHandler에 의해 핸들링이 된 상황입니다! 그리고 이제 원래대로 라면 부모로 예외를 전파 해야 하지만, SupervisorJob() 에 의해 부모로 전파는 되지 않습니다. 따라서 자식 코루틴이 중간에 종료되지는 않는 상황이에요!

 

답변이 도움이 되었으면 좋겠습니다. 또 언제든 궁금한 점 있으시면 편하게 질문 주세요!

감사합니다! 🙏

윤성식님의 프로필 이미지
윤성식
질문자

너무나 상세한 답변 감사드립니다 🙂 ~!!

윤성식님의 프로필 이미지
윤성식

작성한 질문수

질문하기