Coroutine Dispatchers

Photo by Matt Duncan on Unsplash

Coroutine Dispatchers

·

3 min read

  • Enable us to decide on which thread (or pool of threads) a coroutine should be running ie. starting and resuming.

  • Provided as a part of the coroutine context within a scope.

  • CoroutineDispatcher implements the Element interface which holds a Key.

    • That's why CoroutineDispatcher can be used as a key itself to know the running dispatcher details.

Type of dispatchers:

  • There are four primary types of Dispatchers available:

    • Dispatchers.Default: Used by standard builders like launch{}, async{}, etc.

      • uses when no specific dispatcher or ContinuationInterceptor is specified in the coroutine's context.
    • Dispatchers.Main: Operates exclusively on the Main thread and is dedicated to handling UI objects

      • typically a single-threaded dispatcher.
    • Dispatchers.IO: Specialized CoroutineDispatcher designed for offloading blocking IO tasks to a shared pool of threads.

    • Dispatchers.Unconfined: A coroutine dispatcher that is not confined to any particular thread.

      • executes the initial continuation of the coroutine in the current call frame

        • allows the coroutine to resume in any thread that is fully determined by the suspending function that was invoked, without imposing any specific threading policy.
      • appropriate for coroutines which neither consume CPU time nor update any shared data (like UI) confined to a specific thread.

Default Dispatcher:

API Signature

@JvmStatic
public actual val Default: CoroutineDispatcher = DefaultScheduler

Usage example

val scope = CoroutineScope(Dispatchers.Default)
  • If no dispatcher is explicitly provided to the coroutine scope

    • the default dispatcher is implicitly used i.e. Dispatchers.Default.
  • It's designed to handle CPU-intensive operations.

  • It operates using a pool of threads

    • equal to the number of cores on the machine where the code is running

      • with a minimum of two threads.
  • Shares its threads with a Dispatchers.IO dispatcher

    • important to note that using withContext(Dispatchers.IO) { ... } does not lead to an actual switching to another thread.

Main dispatcher:

API Signature

@JvmStatic
public actual val Main: MainCoroutineDispatcher
    get() = MainDispatcherLoader.dispatcher
  • Uses for UI application that runs on the main thread, e.g., Android application, Swing, JavaFX application.

  • kotlinx-coroutines-android library provides an API specifically designed for the Android

    • Uses for UI application which runs on main thread, e.g., Android application, Swing, JavaFx application.

    • kotlinx-coroutines-android library provides an API specifically designed for Android

    • immediate main dispatcher

      • When a new dispatcher is assigned and it matches the parent's running dispatcher

        • No context switching occurs, and re-dispatching is avoided.
      • immediate dispatcher serves as a performance optimization, eliminating unnecessary dispatching.

      • Optimization saves resources and time that would be spent on creating a new dispatcher.

      • If immediate dispatching is not supported by the current dispatcher

        • UnsupportedOperationException exception is thrown.
public abstract val immediate: MainCoroutineDispatcher

Usage example

withContext(Dispatchers.Main.immediate) {
    //ui work
}

Difference between IO and Default Dispatcher

  • The main difference between these two dispatchers can be summarized as follows

    • Default Dispatcher: Preferred choice when it comes to off-loading operations from the main thread, especially for CPU-intensive tasks.

By default, the maximal level of parallelism used by this dispatcher is equal to the number of CPU cores, but is at least two.
Level of parallelism X guarantees that no more than X tasks can be executed in this dispatcher in parallel.

  • IO Dispatcher: Preferred choice for handling heavy IO operations, such as reading/writing files, uploading files, and encrypting/decrypting files

    • ensure smooth execution without blocking the main thread.

The number of threads used by tasks in this dispatcher is limited by the value of “kotlinx.coroutines.io.parallelism” (IO_PARALLELISM_PROPERTY_NAME) system property.
It defaults to the limit of 64 threads or the number of cores (whichever is larger).

Happy coding...😊