본문 바로가기
프로그래밍 놀이터/Kotlin

[Kotlin] Coroutine 은 어떻게 동작하는가?

by 돼지왕왕돼지 2020. 8. 23.



코루틴은 어떻게 동작하는가?


-

돼욍 Comment

Coroutine 이 StateMachine 형태로 동작함을 rough 하게 이해할 수 있다.

StateMachine 의 상태 변화에 따라 값을 save & fetch 한다는 것을 rough 하게 이해할 수 있다.

Continuation 이라는 것을 재활용해 Dispatcher 를 포함한 context 를 계속 물고 갈 수 있음을 rough 하게 이해할 수 있다.



-

Kotlin 의 Coroutine 은 suspend 키워드로 마킹된 함수를 CPS(Continuation Passing Style)로 변환하고, 이를 Coroutine Builder 를 통해 적절한 스레드 상에서 시나리오에 따라 동작하도록 구성된다.



-

suspend function 은 스레드와 스케줄의 관리를 수행하는 것이 아니라 비동기 실행을 위한 중단(suspension) 지점의 정의이다.

코루틴은 중단 지점까지 비선점형으로(cooperative) 동작하기 때문에 실행 스케줄이 OS 에 의해 온전히 제어되는 스레드(preemptive)와는 다른 관점에서 보아야 한다.



-

일반적인 subroutine 은 단일 지점에서 시작해서 특정 지점에서 종료한다.

coroutine 은 단일 지점에서 시작, 임의 지점에서 멈춤, 해당 지점에서 재개, 특정 지점에서 종료 가능하다.



-

Kotlin 에서는 sequencial code 를 통해 non-blocking 코드를 작성하기 위한 수단으로 coroutine 제공



-

상태를 저장함으로써 특정 지점에서 다시 시작할 수 있다. (callback 의 개념과 비슷하긴 하다.)

이 상태저장을 Continuation Passing Style (CPS) 라고 한다.



-

public interface Continuation<in T>{
    val context:CoroutineContext // 어떤 스레드에서 수행할 것인가?

    fun resume(value:T)
    fun resumeWithException(exception:Throwable)
}



-

suspend fun fetchUserDetail(id:String){
    val token = auth()
    val user = getUser(token, id)
    updateUserData(user)
}


위 코드가 아래와 같은 느낌으로 state 를 저장한다고 볼 수 있음

fun fetchUserDetail(id:String, cont:Continuation){ // state machine val sm = cont as? thisSM ?: object: CoroutineImpl{ // continuation reuse fun resume(…){ fetchUserDetail(null, this) // resume callback } } swith(sm.label){ case 0: sm.id = id sm.label = 1 // labeling auth(sm) case 1: val id = sm.id val token = sm.result as String sm.label = 2 // labeling getUser(token, id, sm) case 2: ... } }


-

CPS(Continuation Passing Style) transform


StateMachine 에 의해 코드의 실행 위치 처리

함수에 대한 진입/진출 시 상태를 저장/복원하기 위한 코드

Continuation reuse



-

class DispatchedContinuation<in T>(val dispatcher:CoroutineDispatcher, val continuation:Continuation<T>): Continuation<T> by continuation{
    override fun resume(value:T){
        dispatcher.dispatch(context, DispatchTask(…))
    }
}


-

끝!!!




댓글0