본문 바로가기
프로그래밍/kotlin

[Kotlin] Coroutine 의 시작 순서에 대해 깊이 알아보자 (BaseContinuationImpl, AbstractCoroutine)

by dev_gyu 2025. 5. 19.
728x90

우리는 코루틴을 사용하면서 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 상태라면 이를 실행시키는 방법은 다음과 같다.

  1. 자식이 생성되어 initParentJob 의 parent.start() 를 호출하고, 이를 통해 startInternal() 진입 -> onStart() 호출
  2. Job.start() 로 진행되거나 join() 을 통해 joinInternal() 에 진입, 이를 통해 startInternal() 에 진입하여 onStart() 가 호출되는 순서이다.

# 코루틴 실행

CoroutineStart.DEFAULT, LAZY 를 타게 될 경우 실행되는 함수
CoroutineStart.ATOMIC 을 타게되는 경우 실행되는 함수
CoroutineStart.Undispatched 를 타게 되면 실행되는 함수

결국 전체적으로 CoroutineStart.invoke() 를 실행하게 되면 여러 createCoroutine 함수를 통해 Continuation 객체를 생성하고, BaseContinuationImpl.resumeWith(result: Result<Any?>) 를 실행하여 결과값을 전달된 completion.resumeWith (부모 Continuation) 에 result 를 전달하게 된다.

AbstractCoroutine.resumeWith

 

위의 코드에서 completion 은 AbstractCoroutine 이므로, BaseContinuationImpl.resumeWith 실행 -> AbstractCoroutine.resumeWith 이 실행되고 내부적으로 상태머신을 업데이트하여 최종 결과를 Return 하게된다.

# 요약

  1.  launch 나 async 와 같은 함수 내부에서는 AbstractCoroutine.start() 를 호출하게 된다.
  2. 개발자가 작성한, 혹은 기본적으로 설정되어 있는 CoroutineStart 를 전달받은 AbstractCoroutine.start() 는 CoroutineStart.invoke() 를 실행하고 completion 으로 자기 자신을 전달한다.
  3. CoroutineStart 내부의 조건문에 따라 각기 다른 방식으로 Continuation 객체를 생성, 스케줄링하고BaseContinuationImpl.resumeWith 이 실행된다.
  4. BaseContinuationImpl.resumeWith 결과값을 변수 result 를 저장하고, 이를 다시 completion (AbstractCoroutine).resumeWith(result) 으로 실행한다.
  5. AbstractCoroutine.resumeWith 이 실행되고 내부적으로 상태머신 업데이트를 통해 Finishing 단계에 진입하고 예외 전파, 완료 처리 등을 진행하며 코루틴은 부모 및 자식 코루틴의 핸들러가 모두 종료되면 (모두 실행되면) 최종 결과를 Return 한다.
728x90