协程是 Kotlin 的核心异步能力,而 Dispatchers
则决定了协程在哪里、如何运行。
什么是 Dispatcher?
在 Kotlin 协程中,Dispatcher
决定了协程使用哪个线程或线程池执行。就像把任务分派到不同的"执行员"手里,不同的 Dispatcher 有不同的"专长"。
协程 = 轻量线程,而 Dispatcher = 线程调度器
常用的 Dispatchers 有:
Dispatchers.Default
Dispatchers.IO
Dispatchers.Main
(仅限 Android 或支持 UI 的平台)
1. Dispatchers.Default
源码分析
Dispatchers.Default对应的实现位于DefaultScheduler
DefaultScheduler继承自SchedulerCoroutineDispatcher,并且传入参数:
- corePoolSize = cpu核心数
- MAX_POOL_SIZE = 2M
- idleWorkerKeepAliveNs = 60s
从注释说明来看,当新的任务到来时,最大只有corePoolSize个Worker可以被创建
js
* When a new task arrives in the scheduler (whether it is local or global queue),
* either an idle worker is being signalled, or a new worker is attempted to be created.
* (Only [corePoolSize] workers can be created for regular CPU tasks)
总结
- 使用共享的后台线程池(基于 CPU 核心数)
- 适合 CPU 密集型 操作(如排序、加密、JSON 解析等)
- 线程数量默认是
Runtime.getRuntime().availableProcessors()
2. Dispatchers.IO
DefaultIoScheduler分发任务时,实际上使用了UnlimitedIoScheduler.limitedParallelism创建的CoroutineDispatcher,并且传入64
LimitedDispatcher.kt 最大会创建parallelism个worker用于执行任务
实际也是调用DefaultScheduler执行,不过传入的是BlockingContext,这个是跟Dispatchers.Default的区别所在,Dispatchers.Default是NonBlockingContext。
总结
- 专为 IO 密集型 任务设计(文件、网络、数据库)
- 使用可增长线程池(最多 64 个线程)
- 会避免
Default
的线程被 IO 阻塞
错误使用 Dispatchers 的性能风险
示例:将 CPU 密集任务放到 Dispatchers.IO
js
// 错误示例:将大量 CPU 计算任务放在 IO 线程池
launch(Dispatchers.IO) {
repeat(100_000) {
heavyComputation(it)
}
}
1. IO 调度器并不为 CPU 密集任务设计
Dispatchers.IO 的线程池规模大(最大 64 线程),适合阻塞型任务(如文件、网络读取),而非频繁执行的 CPU 密集计算。
2. 上下文频繁切换(Context Switch)开销大 IO 调度器的线程数远高于 CPU 核心数,当你把密集计算放在里面时,操作系统将频繁在多个线程间切换 CPU 使用权,带来:
- CPU cache 无法复用(尤其是 L1/L2 缓存失效)
- 系统调度器负载增加
- 实际执行速度反而变慢
3. 调度器饱和,真正的 I/O 请求被阻塞 计算任务长时间占用 IO 线程,导致后续的真正阻塞操作(如文件读取)排队,影响整个系统响应时间。