调度器与并发限制
调度器 (Dispatchers) 决定了协程在哪个线程或线程池上执行。Kotlin 协程的设计哲学是 Explicit Switching (显式切换)。
标准调度器
| 调度器 | 底层实现 | 适用场景 | 线程数上限 |
|---|---|---|---|
Main | Handler (Android) | UI 更新、轻量级交互 | 1 (主线程) |
IO | 弹性线程池 | 网络、磁盘、数据库 | 64 或 CPU 核心数 (取大值) |
Default | 固定大小线程池 | CPU 密集型计算 (JSON解析、Diff) | CPU 核心数 |
Unconfined | 无 | 极低开销任务 (测试/日志) | N/A |
IO 与 Default 的弹性机制
线程池共享
Dispatchers.IO 和 Dispatchers.Default 实际上共享同一个底层线程池 (CoroutineScheduler)。 这允许线程在执行完 IO 任务后,直接复用以执行 CPU 计算任务,完全消除了线程切换和上下文损耗。
线程切换:withContext
withContext 不仅仅是切换线程,它是一个挂起函数。
- 它会等待代码块执行完毕。
- 它会检查结果返回后的取消状态。
kotlin
suspend fun loadAndShow() {
// 1. 在主线程启动 (假设)
showLoading()
// 2. 挂起主线程,切换到 IO 线程执行
val data = withContext(Dispatchers.IO) {
// 这里的代码运行在 IO 线程
loadFromDb()
}
// 3. 自动切回主线程
showContent(data)
}并发限流:limitedParallelism
Kotlin 1.6 引入的杀手级 API。它不仅能限制并发数,还能用来创建更轻量的单线程调度器(用于处理线程不安全的资源)。
kotlin
// 场景:数据库连接池只有 4 个连接,必须限制同时访问
val dbDispatcher = Dispatchers.IO.limitedParallelism(4)
suspend fun writeToDb() = withContext(dbDispatcher) {
// 即使启动 100 个协程,同时也只有 4 个在执行
transaction { ... }
}替代单线程
limitedParallelism(1) 是替代 newSingleThreadContext 的更优解。它不会真正创建一个新线程,而是复用 IO 线程池中的线程并保证串行执行,避免了创建线程的昂贵开销。