Coroutine 에 대해 알아보자 (3)

차경민
쓱싹팀 이야기
6 min readJul 24, 2022
Photo by Siora Photography on Unsplash

이 포스터는 코루틴 공식 가이드 자세히 읽기 — Part 5 를 읽고 재정리한 내용입니다.

이 글의 주요 내용은 Coroutine Context 와 Dispatcher 입니다.

Disptchers and threads

CoroutineContext는코루틴 실행시 사용할 스레드, 스레드 풀을 결정짓는 Disptcher를 포함하고 있습니다.
Disprtcher는 코루틴의 실행을 특정 쓰레드에 한정짓거나, 특정 쓰레드 풀로 전달하거나, 스레드의 제한 없이 실행되도록 할 수 있습니다.

CoroutineContext는 코루틴 빌더에 CoroutineContext가 전달이 가능하며 이외에 Dispatcher나 Context요소들도 지정이 가능합니다.

<결과>

Unconfined : I’m working in thread main
Default : I’m working in thread DefaultDispatcher-worker-1
main runBlocking : I’m working in thread main
newSingleThreadContext: I’m working in thread test

launch : laucnh 빌더가 피라미터 없이 호출될 경우 실행된 코루틴으로 부터 상속받은 Context를 사용합니다. (runBlocking)

Dispatchers.Unconfined : 메인 스레드에서 실행되는 것 처럼 보이지만, 다른 매커니즘을 사용합니다. 이는 잠시후에 설명합니다.

Dispatchers.Default : GlobalScope에서 실행될 경우 공통으로 사용되는 백그라운드 풀을 사용되며 동일한 디스패쳐를 사용합니다.

newSingleThreadContext : 새로운 코루틴이 생성되면 새로운 스레드가 만들어집니다. 코루틴에 전용 스레드를 사용하는 것은 리소스 낭비이기 때문에 더이상 사용하지 않을 경우 close 함수 및 Top-level 에 저장하고 재사용해야 합니다.

스레드 한정 디스패처와 비-한정 디스패처

Dispatchers.Unconfined 는 호출 스레드에서 코루틴을 시작하지만 중단함수로 중단될경우 재게된 스레드에서 수행됩니다.
비-한정 디스패쳐인 경우 코루틴이 CPU 시간을 소모하지 않거나 UI 업데이트를 하지않은 경우 처럼 특정 스레드에 국한된 작업이 아닌경우 적절합니다.

코루틴은 부모 — 자식 코루틴 계층 구조로 정의될 때 자식 코루틴은 부모 코루틴의 Context 를 상속받습니다.

<결과>

runBlocking을 상속한 코루틴은 메인 스레드에서 게속 실행되지만 Unconfined 코루틴은 delay 함수가 사용하는 default executor 스레드에서 실행이 재개 됩니다.

Unconfined dispatcher는 자신이 뜻하지 않은 방향으로 실행될 수 있습니다.
그래서 특수한 상황에서만 사용해야 한다.

코루틴과 스레드 디버깅 하기

코루틴이 어디서 언제 실행되고 있는지 알아내기는 어려운 일입니다.
이때 손쉽게 디버깅할 수 있는 방법이 있는데

run -> edit configuration -> Dkotlinx.coroutines.debug 를 추가하면 됩니다.

Dkotlinx.coroutines.debug 를 추가하기

<출력 결과>

스레드와 coroutine의 id가 출력된것을 볼 수 있습니다.

스레드 전환

<출력 결과>

newSingleThreadContext 는 코루틴이 생성되면 스레드도 생성된다고 위에서 말했습니다. 위의 예제에서는 use()함수를 사용하여 생성된 스레드가 더이상 필요하지 않으면 해제하고 있습니다.

Job 획득

코루틴에서는 coroutineContext[Job]의 표현을 이용하여 Job을 획득할 수 있습니다.

<출력 결과>

자식 코루틴

어떤 코루틴이 다른 코루틴 스코프 안에서 생성되면 부모 코루틴의 컨텍스트를 상속받고, 자식 코루틴의 Job은 부모 코루틴의 Job의 자식으로 생성됩니다
그래서 부모 쿠리틴이 취소되면 자식 코루틴들도 재귀적으로 취소됩니다.

하지만 GlobalScope에서 실행된 코루틴은 독립적으로 동작됩니다.
아래 예제를 보면 GlobalScope에서 실행된 코루틴은 자신이 실행된 스코프가 취소되어도 영향받지 않은다는 것을 알 수 있습니다.

<실행 결과>

부모 코루틴의 의무

부모 코루틴은 자식 코루틴이 실행될 때 까지 기다립니다.
이를 위해 명시적으로 추적할 필요 없으며 자식들의 종료하기 위해 Job.join을 할 필요도 없습니다.

<실행 결과>

코루틴 이름 지정

자동으로 설정된 id는 코루틴 로그를 확인할 때 편하지만, 명시적으로 지정하는 것이 디버깅 할 때 훨신 도움이 됩니다.
CoroutineName이라는 Context 요소로 코루틴 이름을 지정할 수 있습니다.

<실행 결과>

Context 병합하기

코루틴 Context를 병합해야할 때가 있습니다.
이 때 +(plus) 연산자를 사용하여 병합할 수 있습니다.

<실행결과>

--

--