우리는 코루틴을 사용하면서 CoroutineScope 내에 launch 나 asnyc 등을 이용하면 코루틴 디스패처에 스케줄링된 빌더가 실행된다는 것을 알고 있다.
하지만 나는 코루틴의 실행이 Continuation.resumeWith 을 통해 진행된다는 것을 알고 있었으나 이 과정이 자세하게 어떤 방식으로 진행되는지에 대해 잘 모르고 있었다.
이에 대해 이번 글을 작성해보았으므로 코루틴의 시작 순서에 대해 자세히 알아보자
이번 과정에서 CoroutineStart.Lazy 의 경우 이전 게시글의 JobSupport.kt 의 AttachChild 관련 글이 약간이나마 도움이 될 수 있으니 이를 참고하면 될 듯 하다.
2025.05.16 - [프로그래밍/kotlin] - [Kotlin] Coroutine Structured Concurrency (구조화된 동시성) 에 대해 알아보자
[Kotlin] Coroutine Structured Concurrency (구조화된 동시성) 에 대해 알아보자
들어가기에 앞서, 우선 아래와 같이 테스트 코드를 하나 만들어보자class DevGyuTest { @Test fun test() = runTest { launch { launch { delay(1000) println("실행1") } launch { delay(3000) println("실행2") } delay(1500) println("부
dev-gyu.tistory.com
# 코루틴의 시작 과정
코루틴의 예외 전파에 대해 알기 전, 우리는 코루틴의 시작 과정이 어떻게 되는지부터 알아야한다.
우리가 코루틴을 실행시킬때는 CoroutineScope 내에서 launch 나 async 를 실행시킨다.
이러한 launch 와 async 는 내부적으로 AbstractCoroutine.start() 를 사용하는데, 이는 아래와 같다.
CoroutineStart.invoke() 로 block, receiver, completion 을 전달하며 completion 은 자기 자신이다.
이후 전달된 CoroutineStart 값에 따라 각기 다른 방식의 코루틴으로 시작하게 된다.
# LAZY 의 실행
LAZY 상태의 경우 아무 작업도 하지 않고 대기하다가 JobSupport.kt 에 존재하는 start() 함수를 타야 실행이 진행된다.
위의 public final override fun start() 로 진입하게 되며, startInternal 내의 onStart() 를 통해 continuation 객체에 Coroutine 생성되어있던 코루틴을 시작시킨다.
위의 Lazy 함수들의 onStart() 가 실행되어 첫 suspend 이전에도 취소 가능한 코루틴이 시작되게 되는 것이다.
즉, 만약 Lazy 상태라면 이를 실행시키는 방법은 다음과 같다.
- 자식이 생성되어 initParentJob 의 parent.start() 를 호출하고, 이를 통해 startInternal() 진입 -> onStart() 호출
- Job.start() 로 진행되거나 join() 을 통해 joinInternal() 에 진입, 이를 통해 startInternal() 에 진입하여 onStart() 가 호출되는 순서이다.
# 코루틴 실행
결국 전체적으로 CoroutineStart.invoke() 를 실행하게 되면 여러 createCoroutine 함수를 통해 Continuation 객체를 생성하고, BaseContinuationImpl.resumeWith(result: Result<Any?>) 를 실행하여 결과값을 전달된 completion.resumeWith (부모 Continuation) 에 result 를 전달하게 된다.
위의 코드에서 completion 은 AbstractCoroutine 이므로, BaseContinuationImpl.resumeWith 실행 -> AbstractCoroutine.resumeWith 이 실행되고 내부적으로 상태머신을 업데이트하여 최종 결과를 Return 하게된다.
# 요약
- launch 나 async 와 같은 함수 내부에서는 AbstractCoroutine.start() 를 호출하게 된다.
- 개발자가 작성한, 혹은 기본적으로 설정되어 있는 CoroutineStart 를 전달받은 AbstractCoroutine.start() 는 CoroutineStart.invoke() 를 실행하고 completion 으로 자기 자신을 전달한다.
- CoroutineStart 내부의 조건문에 따라 각기 다른 방식으로 Continuation 객체를 생성, 스케줄링하고BaseContinuationImpl.resumeWith 이 실행된다.
- BaseContinuationImpl.resumeWith 결과값을 변수 result 를 저장하고, 이를 다시 completion (AbstractCoroutine).resumeWith(result) 으로 실행한다.
- AbstractCoroutine.resumeWith 이 실행되고 내부적으로 상태머신 업데이트를 통해 Finishing 단계에 진입하고 예외 전파, 완료 처리 등을 진행하며 코루틴은 부모 및 자식 코루틴의 핸들러가 모두 종료되면 (모두 실행되면) 최종 결과를 Return 한다.