or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

channels.mdcoroutine-builders.mddispatchers.mdexception-handling.mdflow-api.mdindex.mdjobs-deferreds.mdstructured-concurrency.mdsynchronization.md

dispatchers.mddocs/

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

```