Post

(Kotlin Coroutine) Dispatchers

Dispatcher란?

[!info] The coroutine dispatcher can confine coroutine execution to a specific thread, dispatch it to a thread pool, or let it run unconfined.
즉 코루틴이 어떤 thread(thread pool) 위에서 실행될지를 결정하는 역할.

기본 제공 Dispatchers

Dispatchers.Default

  • The default CoroutineDispatcher that is used by all standard builders like launch, async, etc if no dispatcher nor any other ContinuationInterceptor is specified in their context.
  • CPU core 수와 동일한 스레드 수로 구성된 스레드풀. core가 1개인 경우는 2개로 구성.
  • CPU bound 작업에 사용.

Dispatchers.IO

  • IO bound 작업에 사용.
  • This dispatcher shares threads with a Default dispatcher, so using withContext(Dispatchers.IO) { ... } does not lead to an actual switching to another thread — typically execution continues in the same thread. 👍 (thread switching은 비싸기 때문에. 최소화 하는 것이 좋다.)

Dispatchers.Main (MainScope())

  • 안드로이드 같은 UI 포함된 개발에서 사용. BE 개발에서는 쓸 일이 없음.
  • UI 관련 동작하는 Main thread에서 돌아가도록 한정 할 때 사용.
  • e.g., 화면 표시를 담당하는 Activity가 종료되는 경우 관련 코루틴들을 한꺼번에 종료해야 할 때

Dispatchers.Unconfined

  • A coroutine dispatcher that is not confined to any specific thread. It executes initial continuation of the coroutine in the current call-frame and lets the coroutine resume in whatever thread

thread pool 고갈 방지 (직접 thread pool, Dispatcher 생성)

  • 예를 들어 DB library가 coroutine 지원하지 않아 blocking call 인 경우, thread pool의 모든 thread가 block되어 새로운 request를 처리 할 수 없게 되는 상황을 방지해야 한다.
  • Dispatchers.IO에 던지는 것으로 시스템 전체가 block 되는 것은 막을 수 있다. 하지만, DB call로 인해 Dispatchers.IO 스레드 풀 전체가 block 되면, Dispatchers.IO를 사용하는 다른 작업(e.g., HTTP call)들도 영향을 받는다.

IO 풀에서 최대 쓸 수 있는 스레드 수 제한

1
private val myDatabaseDispatcher = Dispatchers.IO.limitedParallelism(10)  // At most 10 connections to the database

아예 thread pool을 완전히 분리

  • 또는 절대 밀리면 안되는 코어한 작업이라, 다른 작업으로 인한 영향을 아예 제거하고 싶을 수도 있다. (다른 작업으로 인해 IO 스레드풀이 고갈될 가능성도 있으므로)
  • 이런 경우 전용 스레드풀로 완전히 분리하는게 안전하다
newScheduledThreadPool()
1
2
3
private val coreWorkDispatcher = newFixedThreadPoolContext(10)
is equivalent to 
private val coreWorkDispatcher = Executors.newScheduledThreadPool(10).asCoroutineDispatcher()
  • delay 같은 기능의 built-in yield support를 위해서는 newScheduledThreadPool() 여야 한다.
  • If the given ExecutorService is an instance of ScheduledExecutorService, then all time-related coroutine operations such as delay, withTimeout and time-based Flow operators will be scheduled on this executor using schedule method
newFixedThreadPool()
1
private val coreWorkDispatcher = Executors.newFixedThreadPool(10).asCoroutineDispatcher()
  • 대부분의 경우는 newScheduledThreadPool() 사용하면 되는데,
  • 만약 coroutine 스케줄링과 무관한, standard JDK executors 기반의 스레드 스케줄링 정책으로 돌아가는 스레드풀을 만들고 싶다면, newFixedThreadPool 사용해야 한다.
This post is licensed under CC BY 4.0 by the author.