코틀린 코루틴 라이브러리가 제공하는 중요한 기능은 코루틴이 실행되어야 (시작하거나 재개하는 등) 할 스레드(또는 스레드 풀)를 결정할 수 있다는 것이다. 디스패처를 이용해 이러한 기능을 사용할 수 있다.
영어 사전에서 디스패처는 ‘사람이나 차량, 특히 긴급 차량을 필요한 곳에 보내는 것을 담당하는 사람’이라 정의되어 있다. 코틀린 코루틴에서 코루틴이 어떤 스레드에서 실행될지 정하는 것은 CoroutineContext이다.
디스패처는 RxJava의 스케쥴러(RxJava Schedulers)와 비슷한 개념이다.
디스패처를 설정하지 않으면 기본적으로 설정되는 디스패처는 CPU 집약적인 연산을 수행하도록 설계된 Dispatchers.Default다. 이 디스패처는 코드가 실행되는 컴퓨터의 CPU 개수와 동일한 수(최소 두 개 이상)의 스레드 풀을 가지고 있다. 스레드를 효율적으로 사용하고 있다고 가정하면 (예를 들면 CPU의 집약적인 연산을 수행하며 블로킹이 일어나지 않는 환경) 이론적으로는 최적의 스레드 수라고 할 수 있다.
디스패처를 실제로 보기 위해 다음 코드를 실행해보자.
suspend fun main() = coroutineScope {
repeat(1000) {
launch { // 또는 launch(Dispatchers.Default)
List(1000) { Random.nextLong() }.maxOrNull()
val threadName = Thread.currentThread().name
println("Running on Thread: $threadName")
}
}
}
CPU 8개, 논리 프로세서가 16개인 컴퓨터에서 예제를 실행한 결과는 다음과 같다.
Running on Thread: DefaultDispatcher-worker-16
Running on Thread: DefaultDispatcher-worker-11
Running on Thread: DefaultDispatcher-worker-13
Running on Thread: DefaultDispatcher-worker-7
Running on Thread: DefaultDispatcher-worker-8
Running on Thread: DefaultDispatcher-worker-6
Running on Thread: DefaultDispatcher-worker-12
Running on Thread: DefaultDispatcher-worker-1
Running on Thread: DefaultDispatcher-worker-2
Running on Thread: DefaultDispatcher-worker-14
Running on Thread: DefaultDispatcher-worker-4
Running on Thread: DefaultDispatcher-worker-9
Running on Thread: DefaultDispatcher-worker-5
...
runBlocking은 디스패처가 설정되지 않으면 자신만의 디스패처를 사용하기 때문에 Dispatchers.Default가 자동으로 선택되지 않는다. 위 예제에서 coroutineScope 대신에 runBlocking을 사용하면 모든 코루틴은 main에서 실행된다.
비용이 많이 드는 작업이 Dispatchers.Default의 스레드를 다 써버려서 같은 디스패처를 사용하는 다른 코루틴이 실행될 기회를 제한하고 있다고 의심하는 상황을 떠올려 보자. Dispatchers.Default의 limitedParallelism을 사용하면 디스패처가 같은 스레드 풀을 사용하지만 같은 시간에 특정 수 이상의 스레드를 사용하지 못하도록 제한할 수 있다.
private val dispatcher = Dispatchers.Default.limitedParallelism(5)
디스패처의 스레드 수를 제한하는 방법은 Dispatchers.Default에만 사용되는 것은 아니기 때문에 limitedParallelism을 기억하고 있어야 한다. Dispatchers.IO에서 limitedParallelism이 훨씬 더 중요하며 자주 사용되기 때문이다.
limitedParallelism은 kotlinx.coroutines 1.6 버전에서 도입되었다.
일반적으로 안드로이드를 포함한 애플리케이션 프레임워크는 가장 중요한 스레드인 메인 또는 UI 스레드 개념을 가지고 있다. 안드로이드에서 메인 스레드는 UI와 상호작용하는 데 사용하는 유일한 스레드다. 메인 스레드가 블로킹되면 전체 애플리케이션이 멈추기 때문에 조심스럽게 다뤄야 한다. 메인 스레드에서 코루틴을 실행하려면 Dispatchers.Main을 사용하면 된다.
Dispatchers.Main은 kotlinx-coroutines-android 아티팩트를 사용하면 안드로이드에서 사용할 수 있다. 메인 디스패처를 정의하는 의존성이 없다면 Dispatchers.Main을 사용할 수 없다.
안드로이드에서는 기본 디스패처로 메인 디스패처를 주로 사용한다. 블로킹 대신 중단하는 라이브러리를 사용하고 복잡한 연산을 하지 않는다면 Dispatchers.Main만으로 충분하다. CPU에 집약적인 작업을 수행한다면 Dispatchers.Default로 실행해야 한다.