Coroutine Dispatchers
Coroutines are always executed in a context represented by CoroutineContext
. One of the most important parts of the context is a dispatcher, which determines what thread or thread pool the corresponding coroutine is executed on.
In the IntelliJ Platform, coroutines are executed on three main dispatchers:
Default Dispatcher
The Dispatchers.Default
dispatcher is used for performing CPU-bound tasks.
It ensures that the number of tasks running in parallel does not exceed the number of CPU cores. A hundred threads performing CPU-bound work on a machine with 10 CPU cores can result in threads competing for CPU time and excessive thread switching. This makes the IDE effectively slower, hence the limitation. Using the default dispatcher (or its limitedParallelism()
slice) enables a consistent CPU load.
IO Dispatcher
The Dispatchers.IO
dispatcher is used for performing IO operations like reading/writing to files, network, executing external processes, etc.
It must be used at the very deep moment in the trace right before the actual IO operation happens and exited as soon as the operation is finished. Example:
EDT Dispatcher
The Dispatchers.EDT
dispatcher is used for executing UI actions on the Swing Event Dispatch Thread. Dispatchers.EDT
dispatches onto EDT within the context modality state.
Dispatchers.Main
vs. Dispatchers.EDT
In Kotlin, a standard dispatcher for UI-based activities is Dispatchers.Main
. In the IntelliJ Platform, the EDT dispatcher is also installed as Dispatchers.Main
so both can be used, however always prefer Dispatchers.EDT
. Use Dispatchers.Main
only if the coroutine is IntelliJ Platform-context agnostic (e.g., when it can be executed outside the IntelliJ Platform context). Use Dispatchers.EDT
when in doubt.
Dispatchers vs. Threads
The dispatcher concept is a higher level of abstraction over threads. While the code is always executed on threads, do not think about dispatchers as specific thread instances.
A single coroutine is not bound to the same thread during the whole execution time. It may happen that a coroutine starts on thread A, is suspended, and finished on thread B, even if the whole is executed with the same dispatcher context.
Consider the following code snippet:
The following diagram presents one of the potential execution scenarios:
The code is executed as follows:
suspendingTask
is started and partially executed on Thread 2.suspendingTask
is suspended when it waits for data fetched from the internet.After receiving data,
suspendingTask
is resumed, but now it is executed on Thread 1.Execution explicitly switches to the EDT dispatcher and
updateUI
is executed on EDT.