코루틴 디스패처(Coroutine Dispatchers)의 스레드 전환 원리
코루틴 디스패처(Coroutine Dispatchers)의 스레드 전환 원리
코루틴 디스패처(Coroutine Dispatcher)는 코틀린 코루틴이 어떤 스레드 또는 스레드 풀에서 실행될지를 제어하는 핵심 메커니즘입니다. 디스패처는 ContinuationInterceptor 인터페이스를 구현하여, 모든 Continuation을 재개하기 전에 스레드 전환 로직으로 래핑하는 방식으로 동작합니다. CoroutineDispatcher.dispatch()부터 DispatchedContinuation, DispatchedTask에 이르기까지 디스패처의 내부 동작 원리를 이해하면, 개발자가 별도의 스레딩 코드를 작성하지 않아도 코루틴이 어떻게 스레드 간 전환을 매끄럽게 수행하는지 파악할 수 있습니다. 면접에서도 디스패처의 내부 구조에 대한 질문이 자주 등장하므로, 이 부분을 깊이 이해해 두시면 큰 도움이 됩니다.
이번 면접 질문을 통하여 아래 내용들을 학습하실 수 있습니다.
CoroutineDispatcher가ContinuationInterceptor를 구현하여Continuation을 가로채고 리다이렉트하는 원리DispatchedContinuation이 디스패처와Continuation사이에서 어떤 역할을 수행하는지DispatchedTask.run()의 실행 흐름과 즉시 취소 보장(prompt cancellation guarantee)의 원리- 각 재개 모드(resume mode)의 목적과 디스패치 및 취소 동작에 미치는 영향
Dispatchers.Default,Dispatchers.IO,Dispatchers.Main,Dispatchers.Unconfined의 내부 동작 원리와 올바른 활용 방법
CoroutineDispatcher 기반 클래스
코틀린 코루틴의 모든 디스패처는 CoroutineDispatcher를 상속하며, 이 클래스는 코루틴 컨텍스트 요소(coroutine context element)이자 ContinuationInterceptor 역할을 동시에 수행합니다.
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
public abstract fun dispatch(context: CoroutineContext, block: Runnable)
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
DispatchedContinuation(this, continuation)
}
이 클래스는 디스패칭 파이프라인을 구성하는 세 가지 핵심 역할을 담당합니다.
isDispatchNeeded()는 기본적으로 true를 반환하며, 이 경우 디스패처가 항상 대상 스레드나 스레드 풀로 작업을 스케줄링합니다. 이 메서드를 오버라이드하는 디스패처는 두 가지입니다. Dispatchers.Unconfined는 Continuation을 재개한 스레드에서 바로 실행하기 때문에 항상 false를 반환합니다. Dispatchers.Main.immediate는 호출자가 이미 메인 스레드에 있을 때 이벤트 루프를 거치는 불필요한 왕복을 피하기 위해 false를 반환합니다.
dispatch()는 추상 메서드이자 핵심 동작부입니다. 각 구체 디스패처는 이 메서드를 구현하여 Runnable을 자신의 실행 대상에 스케줄링합니다. 여기서 디스패치되는 Runnable은 DispatchedContinuation 그 자체입니다. DispatchedContinuation은 DispatchedTask를 상속하고, DispatchedTask는 SchedulerTask를 상속하며, SchedulerTask가 Runnable을 구현하는 구조이기 때문입니다.
interceptContinuation()은 final로 선언되어 있으며, 디스패치 메커니즘의 진입점 역할을 합니다. 코루틴 런타임이 Continuation을 생성할 때 컨텍스트에서 ContinuationInterceptor를 확인하고, 존재할 경우 interceptContinuation()을 호출하여 원본 Continuation을 DispatchedContinuation으로 래핑합니다. 이 시점부터 모든 resumeWith() 호출은 디스패칭 레이어를 거치게 됩니다.
DispatchedContinuation: 스레드 전환을 수행하는 래퍼
DispatchedContinuation은 코루틴의 Continuation과 디스패처의 스레드 풀 사이를 연결하는 클래스입니다. 디스패처와 원본 Continuation에 대한 참조를 모두 보유하며, Continuation 인터페이스를 원본에 위임하면서 resumeWith()를 오버라이드하여 디스패칭 로직을 주입합니다.
internal class DispatchedContinuation<in T>(
@JvmField internal val dispatcher: CoroutineDispatcher,
@JvmField val continuation: Continuation<T>
) : DispatchedTask<T>(MODE_UNINITIALIZED), CoroutineStackFrame, Continuation<T> by continuation {
override fun resumeWith(result: Result<T>) {
val state = result.toState()
if (dispatcher.safeIsDispatchNeeded(context)) {
_state = state
resumeMode = MODE_ATOMIC
dispatcher.safeDispatch(context, this)
} else {
executeUnconfined(state, MODE_ATOMIC) {
withCoroutineContext(context, countOrElement) {
continuation.resumeWith(result)
}
}
}
}
}