Coroutines는 언제나 Kotlin 표준 라이브러리에 정의된 CoroutineContext 타입 값으로 표현되는 일부 Context 상에서 실행된다. Coroutine의 Context는 다양한 요소의 집합이다. 주요 요소는 이전 섹션에서 본 Coroutine의 Job과 이번 섹션에서 다룰 Dispatcher이다.
Dispatchers와 Threads
Coroutine Context에는 해당 Coroutine의 실행에 사용되는 단일 스레드나 복수의 스레드를 결정하는 CoroutineDispatcher(CoroutineDispatcher 문서를 확인)가 포함 된다. Coroutine Dispatcher은 Coroutine의 실행될 사용될 스레드를 특정 스레드로 제한하거나 스레드풀에 분배하거나, 제한 없이 실행 되도록 할 수 있다.
launch나 async 같은 모든 Coroutine Builder들은 새로운 Coroutine을 위해 Dispatcher나 다른 Context 요소들을 명시적으로 지정하는데 사용할 수 있는 CoroutineContext 파라미터를 선택적으로 받을 수 있다.
다음의 예를 실행해보자.
launch { // context of the parent, main runBlocking coroutine
println("main runBlocking : I'm working in thread ${Thread.currentThread().name}")
}
launch(Dispatchers.Unconfined) { // not confined -- will work with main thread
println("Unconfined : I'm working in thread ${Thread.currentThread().name}")
}
launch(Dispatchers.Default) { // will get dispatched to DefaultDispatcher
println("Default : I'm working in thread ${Thread.currentThread().name}")
}
launch(newSingleThreadContext("MyOwnThread")) { // will get its own new thread
println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}")
}
📌 전체 코드는 이곳에서 확인할 수 있습니다.
이는 다음의 결과를 출력한다(순서가 다를 수 있음) :
Unconfined : I'm working in thread main
Default : I'm working in thread DefaultDispatcher-worker-1
newSingleThreadContext: I'm working in thread MyOwnThread
main runBlocking : I'm working in thread main
launch { ... }가 파라미터 없이 사용된다면, 실행되는 CoroutineScope으로 부터 Context를 상속 받는다(Dispatcher도 같이). 이런 경우 main 함수의 runBlocking Coroutine으로부터 Context를 상속 받아 Main Thread에서 실행되게 된다.
Dispatchers.Unconfined 또한 Main Thread에서 실행되는 특별한 Dispatcher이지만, 실제로는 나중에 설명될 다른 동작방식에 기인한다.
Default Dispatcher은 Scope내에서 다른 Dispatcher을 사용이 명시적으로 지정되지 않았을 때 사용된다. Dispatchers.Default로 표기되며, 스레드들이 공유하는 Background Pool을 사용한다.
newSingleThreadContext는 Coroutine이 실행되기 위한 새로운 단일 스레드를 생성한다. 전용 스레드는 매우 비싼 리소스이다. 실제 어플리케이션에서 더 이상 필요하지 않을 때 close 함수를 사용해 해제되어야 하며, 최상위 레벨의 변수에 저장하여 어플리케이션이 실행되는 동안 재사용 될 수 있도록 해야 한다.
이 글은 Coroutines 공식 문서를 번역한 글입니다.
원문 : Composing suspending functions - Dispatchers and threads
원문 최종 수정 : 2022년 6월 27일
Unconfined vs confined dispatcher*1
Coroutine Dispatcher인 Dispatchers.Unconfined는 처음 일시 중단 되기 전까지만 호출 스레드에서 Coroutine을 시작한다. 일시 중단 이후에는 실행된 일시 중단 함수에 의해 완전히 결정된 스레드 상에서 Coroutine을 재개한다. Unconfined Dispatcher은 특정한 스레드에서 수행되어야 하는 CPU 시간이나 공유되는 데이터(UI 데이터와 같은)를 업데이트 하지 않는 Coroutines에 적합하다.
반면에, Dispatcher은 기본적으로 바깥 CoroutineScope에 의해 상속 된다. 특히 runBlocking Coroutine에 대한 기본 Dispatcher는 호출 스레드로 제한되므로, 이를 상속하면 예측 가능한 FIFO 스케쥴링*2으로 이 스레드의 실행을 제한할 수 있다.
launch(Dispatchers.Unconfined) { // not confined -- will work with main thread
println("Unconfined : I'm working in thread ${Thread.currentThread().name}")
delay(500)
println("Unconfined : After delay in thread ${Thread.currentThread().name}")
}
launch { // context of the parent, main runBlocking coroutine
println("main runBlocking: I'm working in thread ${Thread.currentThread().name}")
delay(1000)
println("main runBlocking: After delay in thread ${Thread.currentThread().name}")
}
📌 전체 코드는 이곳에서 확인할 수 있습니다.
위는 다음의 결과를 만든다.
Unconfined : I'm working in thread main
main runBlocking: I'm working in thread main
Unconfined : After delay in thread kotlinx.coroutines.DefaultExecutor
main runBlocking: After delay in thread main
runBlocking { ... } 을 상속받은 Context를 가진 Coroutine은 Main Thread에서 실행되지만, Unconfined를 가진 Coroutine은 delay 함수가 사용하는 Default Executor Thread에서 재개된다.
📖 Unconfined Dispatcher은 Coroutine의 일부 작업이 즉시 실행되어야 하기 때문에 나중에 수행하기 위해 Coroutine을 Dispatch 할 필요가 없거나, 원하지 않는 부수효과를 생성하는 특정한 경우에 도움될 수 있는 고급 메커니즘이다. 일반적인 코드에서 Unconfined Dispatcher은 사용되지 말아야 한다.
📖 아래 내용은 독자의 이해를 위해 번역자가 추가한 글입니다.
*1. Unconfined는 제한되지 않은 이라는 뜻이며, Confined는 제한된 이라는 뜻이다. Unconfined Dispatcher는 Coroutine이 실행되는 스레드를 제한하지 않은 Dispatcher을 뜻하며, Confined Dispatcher은 Coroutine이 실행되는 Thread를 제한한 Dispatcher을 뜻한다.
*2. FIFO 스케쥴링이란 먼저 요청한 Coroutine이 자원을 제공 받고, 이미 자원이 사용중이라면 시용이 끝날 때까지 대기하도록 만드는 스케쥴링 기법을 뜻한다.
이 글은 Coroutines 공식 문서를 번역한 글입니다.
원문 : Coroutine context and dispatchers - Unconfined vs confined dispatcher
원문 최종 수정 : 2022년 6월 27일