0
# Dispatchers
1
2
Thread management and execution contexts for controlling where coroutines run. Dispatchers provide the threading policy for coroutine execution and are a key component of the coroutine context.
3
4
## Capabilities
5
6
### CoroutineDispatcher Base Class
7
8
The foundation class for all coroutine dispatchers, providing thread management and execution control.
9
10
```kotlin { .api }
11
/**
12
* Base class to be extended by all coroutine dispatcher implementations.
13
*/
14
abstract class CoroutineDispatcher : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
15
/**
16
* Dispatches execution of a runnable block onto another thread in the given context.
17
* This method should not be used directly.
18
*/
19
abstract fun dispatch(context: CoroutineContext, block: Runnable)
20
21
/**
22
* Returns true if the execution of the coroutine should be performed with dispatch method.
23
* The default behavior for most dispatchers is to return true.
24
*/
25
open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
26
27
/**
28
* Creates a view of the current dispatcher that limits the parallelism to the given value.
29
*/
30
fun limitedParallelism(parallelism: Int): CoroutineDispatcher
31
32
/**
33
* Dispatches execution of a block onto another thread, but immediately returns
34
* without waiting for the completion.
35
*/
36
operator fun plus(other: CoroutineContext): CoroutineContext
37
}
38
```
39
40
### Standard Dispatchers
41
42
The built-in dispatcher implementations provided by kotlinx-coroutines.
43
44
```kotlin { .api }
45
/**
46
* Groups various implementations of CoroutineDispatcher.
47
*/
48
object Dispatchers {
49
/**
50
* The default CoroutineDispatcher that is used by all standard builders
51
* if no dispatcher is specified in their context.
52
*/
53
val Default: CoroutineDispatcher
54
55
/**
56
* A coroutine dispatcher that is confined to the Main thread operating with UI objects.
57
*/
58
val Main: MainCoroutineDispatcher
59
60
/**
61
* A coroutine dispatcher that is not confined to any specific thread.
62
*/
63
val Unconfined: CoroutineDispatcher
64
65
/**
66
* The IO dispatcher that is designed for offloading blocking IO tasks to a shared pool of threads.
67
* Available on JVM and Native targets.
68
*/
69
val IO: CoroutineDispatcher // Platform-specific availability
70
}
71
72
/**
73
* A CoroutineDispatcher for UI components that operate on the main/UI thread.
74
*/
75
abstract class MainCoroutineDispatcher : CoroutineDispatcher() {
76
/**
77
* Returns dispatcher that executes coroutines immediately when it is already
78
* in the right context (on main thread) instead of dispatching them.
79
*/
80
abstract val immediate: MainCoroutineDispatcher
81
}
82
```
83
84
**Usage Examples:**
85
86
```kotlin
87
import kotlinx.coroutines.*
88
89
val scope = MainScope()
90
91
// Default dispatcher - for CPU-intensive work
92
scope.launch(Dispatchers.Default) {
93
val result = heavyComputation()
94
println("Computation result: $result")
95
}
96
97
// Main dispatcher - for UI operations (iOS main queue)
98
scope.launch(Dispatchers.Main) {
99
updateUI("Loading...")
100
101
// Switch to background for work
102
val data = withContext(Dispatchers.Default) {
103
fetchAndProcessData()
104
}
105
106
// Back to main for UI update
107
updateUI("Data: $data")
108
}
109
110
// Main.immediate - avoids dispatch if already on main
111
scope.launch(Dispatchers.Main.immediate) {
112
// This runs immediately if already on main thread
113
println("Quick UI update")
114
}
115
116
// Unconfined dispatcher - not recommended for general use
117
scope.launch(Dispatchers.Unconfined) {
118
println("Starts in calling thread: ${Thread.currentThread().name}")
119
delay(100)
120
println("Resumes in different thread: ${Thread.currentThread().name}")
121
}
122
123
// IO dispatcher (JVM/Native specific)
124
scope.launch(Dispatchers.IO) {
125
val fileContent = readFile("data.txt")
126
val networkResponse = makeNetworkCall()
127
processIOResults(fileContent, networkResponse)
128
}
129
```
130
131
### Dispatcher Configuration
132
133
Customize dispatcher behavior and create limited parallelism views.
134
135
```kotlin { .api }
136
/**
137
* Creates a view of the current dispatcher that limits the parallelism to the given value.
138
* The resulting dispatcher shares threads with the original dispatcher.
139
*/
140
fun CoroutineDispatcher.limitedParallelism(parallelism: Int): CoroutineDispatcher
141
```
142
143
**Usage Examples:**
144
145
```kotlin
146
import kotlinx.coroutines.*
147
148
val scope = MainScope()
149
150
// Limit parallelism for resource-constrained operations
151
val limitedDispatcher = Dispatchers.Default.limitedParallelism(2)
152
153
scope.launch {
154
// Only 2 coroutines can run concurrently on this dispatcher
155
repeat(10) { i ->
156
launch(limitedDispatcher) {
157
println("Task $i starting on ${Thread.currentThread().name}")
158
delay(1000) // Simulate work
159
println("Task $i completed")
160
}
161
}
162
}
163
164
// Create a dispatcher for database operations (limit to 1 for SQLite)
165
val databaseDispatcher = Dispatchers.IO.limitedParallelism(1)
166
167
suspend fun performDatabaseOperation(operation: String) {
168
withContext(databaseDispatcher) {
169
println("Performing $operation on ${Thread.currentThread().name}")
170
// Database operation here
171
delay(100)
172
}
173
}
174
175
scope.launch {
176
// All database operations will be serialized
177
repeat(5) { i ->
178
launch {
179
performDatabaseOperation("Operation $i")
180
}
181
}
182
}
183
184
// Network requests with limited concurrency
185
val networkDispatcher = Dispatchers.IO.limitedParallelism(4)
186
187
suspend fun makeNetworkRequest(url: String): String {
188
return withContext(networkDispatcher) {
189
println("Fetching $url on ${Thread.currentThread().name}")
190
delay(500) // Simulate network delay
191
"Response from $url"
192
}
193
}
194
```
195
196
### Context Switching
197
198
Change execution context during coroutine execution.
199
200
```kotlin { .api }
201
/**
202
* Calls the specified suspending block with a given coroutine context,
203
* suspends until it completes, and returns the result.
204
*/
205
suspend fun <T> withContext(
206
context: CoroutineContext,
207
block: suspend CoroutineScope.() -> T
208
): T
209
```
210
211
**Usage Examples:**
212
213
```kotlin
214
import kotlinx.coroutines.*
215
216
val scope = MainScope()
217
218
scope.launch {
219
println("Starting on: ${Thread.currentThread().name}")
220
221
// Switch to Default dispatcher for CPU work
222
val result = withContext(Dispatchers.Default) {
223
println("Computing on: ${Thread.currentThread().name}")
224
expensiveComputation()
225
}
226
227
println("Back on: ${Thread.currentThread().name}")
228
229
// Switch to Main for UI update
230
withContext(Dispatchers.Main) {
231
println("Updating UI on: ${Thread.currentThread().name}")
232
displayResult(result)
233
}
234
}
235
236
// Chain context switches
237
scope.launch(Dispatchers.Main) {
238
val userData = withContext(Dispatchers.IO) {
239
// Fetch from network
240
fetchUserData()
241
}
242
243
val processedData = withContext(Dispatchers.Default) {
244
// Process data
245
processUserData(userData)
246
}
247
248
// Back to main for UI
249
updateUserProfile(processedData)
250
}
251
252
// Preserve context elements while switching dispatcher
253
scope.launch(Dispatchers.Main + CoroutineName("UserLoader")) {
254
val result = withContext(Dispatchers.Default) {
255
// CoroutineName is preserved, only dispatcher changes
256
println("Processing in coroutine: ${coroutineContext[CoroutineName]?.name}")
257
heavyProcessing()
258
}
259
260
displayResult(result)
261
}
262
```
263
264
### Platform-Specific Dispatchers
265
266
iOS ARM64 specific dispatcher behavior and characteristics.
267
268
**iOS Main Dispatcher:**
269
270
```kotlin
271
// On iOS, Dispatchers.Main is backed by Darwin's main queue
272
scope.launch(Dispatchers.Main) {
273
// Executes on the main queue
274
// Safe for UIKit operations
275
updateUILabel("Hello iOS")
276
}
277
278
// Main.immediate avoids queue dispatch when already on main
279
scope.launch(Dispatchers.Main.immediate) {
280
// If already on main queue, executes immediately
281
// Otherwise dispatches to main queue
282
performImmediateUIUpdate()
283
}
284
```
285
286
**iOS Threading Characteristics:**
287
288
```kotlin
289
import kotlinx.coroutines.*
290
291
// Default dispatcher uses background queues
292
scope.launch(Dispatchers.Default) {
293
// Uses global concurrent queue with QOS_CLASS_DEFAULT
294
println("Running on background thread: ${Thread.currentThread().name}")
295
}
296
297
// For iOS-specific threading needs
298
suspend fun performIOSSpecificWork() {
299
withContext(Dispatchers.Default) {
300
// CPU-intensive work on background queue
301
val result = processLargeDataSet()
302
303
// Switch to main for UI updates
304
withContext(Dispatchers.Main) {
305
updateProgressIndicator(result.progress)
306
}
307
}
308
}
309
```
310
311
### Dispatcher Testing and Debugging
312
313
Tools and techniques for testing and debugging dispatcher behavior.
314
315
```kotlin
316
import kotlinx.coroutines.*
317
import kotlinx.coroutines.test.*
318
319
// Testing with TestDispatchers (from kotlinx-coroutines-test)
320
@Test
321
fun testWithTestDispatcher() = runTest {
322
val testDispatcher = TestCoroutineScheduler()
323
324
launch(testDispatcher) {
325
delay(1000)
326
println("Task completed")
327
}
328
329
// Advance time in test
330
advanceTimeBy(1000)
331
}
332
333
// Debug dispatcher behavior
334
scope.launch {
335
println("Initial context: ${coroutineContext}")
336
337
withContext(Dispatchers.Default) {
338
println("In Default: ${coroutineContext[CoroutineDispatcher]}")
339
340
withContext(Dispatchers.Main) {
341
println("In Main: ${coroutineContext[CoroutineDispatcher]}")
342
}
343
}
344
}
345
346
// Monitor dispatcher performance
347
fun monitorDispatcherUsage() {
348
val dispatcher = Dispatchers.Default.limitedParallelism(2)
349
350
repeat(10) { i ->
351
scope.launch(dispatcher) {
352
val startTime = System.currentTimeMillis()
353
println("Task $i started at $startTime")
354
355
delay(1000)
356
357
val endTime = System.currentTimeMillis()
358
println("Task $i finished at $endTime, duration: ${endTime - startTime}ms")
359
}
360
}
361
}
362
```
363
364
### Best Practices
365
366
Guidelines for effective dispatcher usage in iOS applications.
367
368
**Choosing the Right Dispatcher:**
369
370
```kotlin
371
// UI operations - always use Main
372
scope.launch(Dispatchers.Main) {
373
updateLabel(text)
374
animateView()
375
}
376
377
// CPU-intensive work - use Default
378
scope.launch(Dispatchers.Default) {
379
val result = complexCalculation()
380
381
// Switch back to Main for UI updates
382
withContext(Dispatchers.Main) {
383
displayResult(result)
384
}
385
}
386
387
// File I/O and network calls - use IO (when available) or limited Default
388
val ioDispatcher = Dispatchers.Default.limitedParallelism(4)
389
scope.launch(ioDispatcher) {
390
val data = readFromFile()
391
val response = makeNetworkCall()
392
processResults(data, response)
393
}
394
```
395
396
**Avoiding Common Pitfalls:**
397
398
```kotlin
399
// DON'T: Unnecessary context switching
400
scope.launch(Dispatchers.Main) {
401
withContext(Dispatchers.Main) { // Redundant
402
updateUI()
403
}
404
}
405
406
// DO: Use Main.immediate when appropriate
407
scope.launch(Dispatchers.Main.immediate) {
408
updateUI() // Executes immediately if already on main
409
}
410
411
// DON'T: Blocking operations on Main
412
scope.launch(Dispatchers.Main) {
413
Thread.sleep(1000) // Blocks main thread - BAD!
414
}
415
416
// DO: Switch context for blocking operations
417
scope.launch(Dispatchers.Main) {
418
val result = withContext(Dispatchers.Default) {
419
blockingOperation() // Runs on background thread
420
}
421
updateUI(result) // Back on main thread
422
}
423
424
// DON'T: Excessive context switching
425
scope.launch {
426
withContext(Dispatchers.Default) {
427
withContext(Dispatchers.Main) { // Unnecessary switching
428
withContext(Dispatchers.Default) {
429
computeValue()
430
}
431
}
432
}
433
}
434
435
// DO: Minimize context switches
436
scope.launch {
437
val result = withContext(Dispatchers.Default) {
438
computeValue()
439
}
440
withContext(Dispatchers.Main) {
441
updateUI(result)
442
}
443
}
444
```
445
446
**iOS-Specific Considerations:**
447
448
```kotlin
449
// Handle iOS app lifecycle
450
class iOSCoroutineManager {
451
private val scope = MainScope()
452
453
fun startBackgroundWork() {
454
scope.launch(Dispatchers.Default) {
455
while (isActive) {
456
performBackgroundTask()
457
delay(30000) // 30 seconds
458
}
459
}
460
}
461
462
fun cleanup() {
463
scope.cancel()
464
}
465
}
466
467
// Coordinate with iOS async APIs
468
suspend fun bridgeToiOSAPI(): String = suspendCancellableCoroutine { continuation ->
469
iOSAsyncFunction { result, error ->
470
DispatchQueue.main.async {
471
if let error = error {
472
continuation.resumeWithException(error)
473
} else {
474
continuation.resume(result)
475
}
476
}
477
}
478
}
479
```