코루틴 공식 가이드 자세히 읽기 — Part 1 — Dive 2

Myungpyo Shim
4 min readJan 21, 2019

--

코루틴은 왜 스레드 보다 가볍다고 할까?

Korean [English]

(목차로 돌아가기)

코루틴 하나가 새로 생성되어 실행된다는 것이 그와 동시에 새로운 스레드 또한 생성되는 것을 의미하는 것은 아닙니다. (정확히 말하자면 이것은 코루틴 생성 시 스케쥴러 설정에 따라 다릅니다.) 사실, 코루틴은 스케쥴링 가능한 코드 블록 혹은 이러한 코드 블록들의 집합이라고 볼 수 있습니다. 이해를 돕기 위해서 코루틴이 실행되는 과정을 다음과 같이 그림으로 나타내 보았습니다.

제일 왼쪽에 보면 CoroutineScope 가있습니다. 우리가 어떤 코루틴을 실행하기 위해서는 어떤 코루틴 스코프에 속해 있어야 합니다. 현재 코루틴 스코프가 갖는 컨텍스트(CoroutineContext) 에서 Dispatcher 는 UI Dispatcher 라고 되어 있습니다. 이것은 현재 스코프에서 실행되는 중단 함수들은 UI Thread 에서 수행 된다는 것을 의미합니다.

이제 이 스코프 안에서 코루틴을 하나 만들었습니다 (가운데 있는 보라색 이미지). 이 코루틴은 자신이 실행되는 스코프(부모)의 컨텍스트를 그대로 상속하고 Dispatcher 만 ThreadPoolDispatcher 로 재정의 하였습니다. (재정의 하지 않으면 기본적으로 속해 있는 스코프로부터 모두 상속합니다. 이제 이 코루틴에서 수행되는 함수는 ThreadPoolDispatcher 를 이용하여 워커(백그라운드) 스레드에서 수행됩니다.

이때, launch { } 와 같이 빌더를 실행했을 경우 마지막으로 넘긴 코드 블록{ code block}, 즉, 실제 수행하고자 하는 로직이 담긴 코드 블록은 Continuation 이라는 단위로 만들어집니다. Continuation 은 CPS(Continuation Passing Style)에서 이야기는 Continuation 개념의 구현체 입니다.

TL; DR : 어떤 일을 수행하기 위한 일련의 함수들의 연결을 각 함수의 반환값을 이용하지 않고 Continuation 이라는 추가 파라미터(Callback)를 두어 연결하는 방식으로 Continuation 단위로 dispatcher 를 변경한다거나 실행을 유예한다거나 하는 플로우 컨트롤이 용이해지는 이점이 있습니다.

이렇게 Continuation 으로 변경 된 코드 블럭은 최초에 suspend 상태로 생성 되었다가 resume() 요청으로 인해 resumed 상태로 전환되어 실행 됩니다. Continuation의 재개(resume)가 요청될 때마다 현재 컨텍스트의 dispatcher 에게 dispatch(스레드 전환) 가 필요한지 isDispatchNeeded() 함수를 이용해 확인 한 후 dispatch가 필요하면 dispatch()함수를 호출하여 적합한 스레드로 전달하여 수행됩니다.

지금은 전반적인 구조에 대한 이해를 돕기 위해서 코루틴 생성 시 Dispatcher를 달리 가져갔지만 만약 Dispatcher 를 재정의하지 않고 UI Dispatcher 를 그대로 상속받아 사용한다면 어땠을까요? 그러면 일반적인 함수 호출과 동일하게 수행됩니다. 바로 이것이 코루틴이 경량의 스레드라고 불리는 이유입니다. 코루틴은 Dispatcher 에 의해 실행되는 환경(Thread)이 결정될 수 있지만, 그 자체로는 환경을 새로 구성하거나 변경하지 않습니다.

그래서 공식 가이드에서 본것과 같은 아래와 같은 코드가 OOM(Out-Of-Memory) 없이 동작할 수 있는 것입니다.

위 코드는 코루틴을 10만개 수행하고 있는 코드 입니다. 위 예제에서 launch { } 코루틴 빌더는 Dispatcher 를 재정의 하지 않았기 때문에 현재 스코프(runBlocking)의 Dispatcher 를 그대로 사용합니다. runBlocking 코루틴 빌더는 내부적으로 GlobalScope을 사용하며 Dispatcher 는 BlockingEventLoop 을 사용하는데, 이는 큐를 이용한 이벤트 루프 형태의 Dispatcher 구현입니다. 그래서 위 코드는 실행 스레드에서 이벤트 루프 기반으로 10만번의 이벤트를 발생하여 점(.)을 출력하게 되며 스레드 부하는 없으므로 OOM을 피할 수 있게 됩니다.

끝.

(목차로 돌아가기)

--

--

No responses yet