[Kotlin] Coroutine - Structured Concurrency
by its_TIMI
들어가기전에,
Job 객체 : Coroutine을 표현함.

여기에서 Completing State는 왜 존재하는가? 라는 찌라시를 남기고 다음 강의에서 알려주신다고 하신 최센세,,,
그 이유는 바로바로! 부모 Coroutine & 자식 Coroutine 간의 Structured Concurrency 유지를 위함이다.
그 유지를 위해,
부모 Coroutine은 작업이 완료되었을 때 Completing 상태로 변경됨.(바아로 완료됨! 안하고!)

부모 코루틴이 Completing 상태로 변경되었다가도 한 자식놈이 완료되고 한놈이 예외 발생시키면 Cnacelling 상태로 돌아가야함.
코드로 살펴보자.
1. 첫번째 경우
package coroutine import kotlinx.coroutines.* fun main(): Unit = runBlocking { launch{ delay(500L) printWithThread("A") } launch { delay(600L) throw IllegalArgumentException("코루틴 쉴패~!") } }
[main] A Exception in thread "main" java.lang.IllegalArgumentException: 코루틴 쉴패~! at coroutine.Lec06Kt$main$1$2.invokeSuspend(Lec06.kt:14) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:235) at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:168) at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:474) at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:508) at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:497) at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:595) at kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask.run(EventLoop.common.kt:493) at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:280) at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85) at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59) at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source) at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38) at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source) at coroutine.Lec06Kt.main(Lec06.kt:5) at coroutine.Lec06Kt.main(Lec06.kt) Process finished with exit code 1
첫번째 자식 코루틴 실행하고 두번째 자식 코루틴 실행 중 취소 예외가 아닌 예외가 발생했으므로 부모 코루틴에게도 해당 예외 전파됨.
따라서 첫번째 자식 코루틴의 실행문만 실행되고 두번째 자식 코루틴 예외발생 및 취소, 부모 코루틴도 취소되게된다.
2. 두번째 경우
package coroutine import kotlinx.coroutines.* fun main(): Unit = runBlocking { launch{ delay(600L) printWithThread("A") } launch { delay(500L) throw IllegalArgumentException("코루틴 쉴패~!") } }
Exception in thread "main" java.lang.IllegalArgumentException: 코루틴 쉴패~! at coroutine.Lec06Kt$main$1$2.invokeSuspend(Lec06.kt:14) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:235) at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:168) at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:474) at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:508) at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:497) at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:595) at kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask.run(EventLoop.common.kt:493) at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:280) at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85) at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59) at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source) at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38) at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source) at coroutine.Lec06Kt.main(Lec06.kt:5) at coroutine.Lec06Kt.main(Lec06.kt) Process finished with exit code 1
예외를 발생시키는 두번째 자식 코루틴이 먼저 실행되게되면,
예외가 부모 코루틴으로 전파되고 부모 코루틴은 본인을 canceling 상태로 바꾸고 첫번째 자식 코루틴에도 취소요청을 보내게된다.


첫번째 자식 코루틴은 suspend fun인 delay를 사용했으므로 취소에 협조적이므로 취소요청시 취소가 되고, 메인 프로세스가 종료되게 되는 것임.
코드 실행 순서 : 부모 코루틴 실행 -> 두번째 자식 코루틴 실행 -> 예외 발생 -> 부모에게 예외 전파 -> 부모가 첫번째 자식에게 취소 전파 -> 메인 프로세스 종료
라잌 디스....부모 자식 관계의 코루틴이 라잌 한몸처럼 움직이는 것을 Structured Concurrency라고 함.
공식문서에서는 Structured Concurrency가
- 수많은 코루틴이 유실되거나 누수되지 않도록 보장하며
- 코드 내의 에러가 유실되지 않고 적절히 보고될 수 있도록 보장한다고 한다.
취소와 예외, Structured Concurrency 총정리
- 자식 코루틴에서 예외가 발생할 경우 Structured Concurrency에 의해 부모 코루틴이 취소되고, 부모 코루틴의 다른 자식 코루틴들도 취소된다.
- 자식 코루틴에서 예외가 발생하지 않더라도 부모 코루틴이 취소되면 자식 코루틴들이 취소된다.
- 단, CancellationException은 정상적인 취소로 간주하기 때문에 그 외의 예외들과 다르게 부모 코루틴에게 전파되지 않고 부모 코루틴의 다른 자식 코루틴을 취소시키지도 않음.
블로그의 정보
Dev_TIMI
its_TIMI