当前位置: 首页 > news >正文

Kotlin 协程设计思想(三):Dispatchers 到底是什么?切线程真的只是切线程吗?

—— 从线程池开始,彻底讲透 Dispatchers.IO、Dispatchers.Default、Dispatchers.Main 的设计思想

上一篇我们讲了:

《Job 到底是什么?为什么协程能被取消?》

我们知道:

CoroutineContext ≈ 特殊Map

其中:

Job 负责生命周期管理

而:

Dispatcher 负责协程调度

很多同学天天写:

launch(Dispatchers.IO) withContext(Dispatchers.IO) launch(Dispatchers.Default)

然后记住:

IO 处理网络 Default 处理计算 Main 更新UI

就结束了。

但我最近重新思考协程设计时突然发现:

Dispatchers 真的只是切线程吗?

答案是:

不是。

它实际上代表着:

协程运行策略(Scheduling Strategy)

而线程池只是其中一部分。


一、很多人对 Dispatcher 的理解其实是错的

第一次学协程时:

launch(Dispatchers.IO) { }

教程都会说:

切到IO线程

于是脑子里变成:

Dispatcher = Thread

事实上:

Dispatcher ≠ Thread

Dispatcher 决定的是:

协程应该由谁调度

而不是:

具体在哪个线程执行

二、先理解一个问题

例如:

launch { }

启动的是:

协程

不是:

线程

那么问题来了:

协程最终由谁执行?

答案:

Dispatcher

三、协程和线程到底是什么关系?

很多人容易混淆。


线程:

操作系统资源

例如:

Thread-1 Thread-2 Thread-3

真实存在。


协程:用户态任务

例如:

Coroutine A Coroutine B Coroutine C

本身不占用线程。


于是:

协程 ↓ Dispatcher ↓ 线程

形成关系。


四、Dispatcher 才是真正的调度者

例如:

launch(Dispatchers.IO) { }

实际过程:

Coroutine ↓ Dispatchers.IO ↓ 线程池 ↓ 某个线程执行

所以:

Dispatcher 是调度者

线程是执行者


五、Dispatchers.Main

最容易理解。

launch(Dispatchers.Main) { }

表示:

必须运行在主线程

例如:

textView.text = "Hello"

必须:

Main Dispatcher

否则:

CalledFromWrongThreadException

六、Dispatchers.IO

很多教程:

IO线程池

然后结束。

实际上 Dispatchers.IO 解决的是:

线程阻塞问题

例如:

networkApi.login()
File.readText()
database.query()

这些操作特点:

CPU不忙 线程在等待

例如:

等待网络 等待磁盘 等待数据库

此时:

线程被占着 却没干活

所以:

IO Dispatcher

允许创建更多线程。

默认:

64

或者:

CPU核心数

取较大值。


七、Dispatchers.Default

很多人以为:

Default就是普通线程池

其实:

Default专门处理CPU计算

例如:

for(i in 0..100000000) { }

或者:

图片压缩 JSON解析 加密解密 算法计算

特点:

线程一直忙

所以:

Default线程数:

CPU核心数

附近。

例如:

8核CPU ≈ 8个线程

为什么?

因为:

CPU计算 线程太多没意义

反而:

频繁上下文切换

更慢。


八、为什么 IO 和 Default 要分开?

这是 Kotlin 团队特别经典的设计。

假设只有一个线程池。


场景:

100个网络请求

全部占满线程。


这时候:

图片压缩

来了。

结果:

没有线程

只能排队。


反过来也一样。


所以:

Google直接拆成:

IO Dispatcher Default Dispatcher

把:

等待型任务

和:

计算型任务

彻底隔离。


九、withContext(IO) 真的是切线程吗?

很多人:

withContext(IO)

就认为:

切线程

实际上:

切的是Dispatcher

例如:

withContext(Dispatchers.IO)

本质:

创建新的CoroutineContext

相当于:

currentContext + Dispatchers.IO

然后:

Dispatcher覆盖

得到:

新的运行环境

十、为什么 launch(IO) 和 withContext(IO) 不一样?

这个面试特别喜欢问。


launch

launch(IO)

返回:

Job

特点:

开启新协程

withContext

withContext(IO)

特点:

不创建新协程

只是:

挂起当前协程

然后:

切换Dispatcher

执行完再回来。


所以:

launch = 开新车

withContext = 换车道


十一、为什么 Dispatchers 也放在 Context 里面?

上一篇讲过:

CoroutineContext ≈ 特殊Map

例如:

{ Job Dispatcher Name ExceptionHandler }

所以:

SupervisorJob() + Dispatchers.IO

实际上:

不是加法

而是:

配置协程运行环境

十二、你项目里其实天天在用

例如:

viewModelScope.launch { }

默认:

Main Dispatcher

然后:

withContext(IO) { }

网络请求。


再:

withContext(Main) { }

更新UI。


本质:

协程没变 Dispatcher在变

十三、CoroutineContext 和 Dispatcher 串起来了

上一篇:

CoroutineContext
是运行环境

这一篇:

Dispatcher
是运行环境中的调度器

所以:

CoroutineContext
决定协程怎么运行

Dispatcher
决定协程在哪运行


十四、最终总结

如果让我一句话解释:

Dispatchers.IO

我不会再说:

IO线程池

而会说:

处理阻塞型任务的调度策略

如果让我解释:

Dispatchers.Default

我会说:

处理CPU密集型任务的调度策略

如果让我解释:

Dispatchers.Main

我会说:

保证任务运行在UI线程的调度策略

所以:

Dispatcher 不是线程 而是协程与线程之间的调度桥梁。

下篇预告

现在:

CoroutineContext✓

Job✓

Dispatcher ✓

都讲完了。

那么问题来了:

launch async withContext 为什么要设计三种启动方式? 它们到底有什么区别? 为什么 async 的异常处理又不一样?

下一篇我们继续:

《Kotlin 协程设计思想(四):launch、async、withContext 到底有什么区别?》

从 Job、Deferred 到 结构化并发 ,彻底讲透 Kotlin 协程三大启动方式的设计思想。

http://www.gsyq.cn/news/1450881.html

相关文章:

  • 从Trinity组装到蛋白预测:手把手教你用TransDecoder v5.7.1搞定转录组ORF分析(附BLAST/Pfam联用技巧)
  • 答辩PPT高效制作技巧:百考通AI助力在校生告别排版内耗
  • [分享]InputBridge 手机玩PC游戏神器!虚拟键盘
  • 从Proteus仿真到PCB打样:一个51单片机电压表的完整开发实战(附ADC0809调试心得)
  • 为什么你的音乐收藏总缺歌词?163MusicLyrics如何解决这个痛点
  • 利用快马平台与oh-my-opencode快速构建可配置的web应用原型
  • 华为交换机LACP配置避坑指南:eNSP实验里那些容易忽略的细节(接口优先级、抢占延迟实战解析)
  • 如何精准下载GitHub文件和目录:DownGit完整解决方案
  • MySQL索引优化宝典:10个案例教你分析慢SQL,让查询速度提升100倍
  • ChatGPT Windows客户端下载与技术架构深度解析(Electron+Vite+React)
  • GBase 8c逻辑解码解析
  • ai-agent 响应速度优化
  • 别再只盯着Gini和OOB了:用Python的sklearn实战对比随机森林特征重要性(附完整代码)
  • 从DeLong检验的数学原理到Python复现:一篇搞懂AUC显著性检验的底层逻辑(附完整代码)
  • 维修公司用什么工单系统比较好?2026年真实对比亲测好用
  • 超越简单分类:用东南大学齿轮箱数据集实战故障严重度评估与迁移学习
  • 用Python从零实现混沌博弈算法(CGO):一个骰子如何帮你优化参数?
  • 作物生长模拟全流程研究:基于WOFOST与PCSE模型的理论、实操与应用对比
  • ASIC压缩加速器技术解析与存储优化实践
  • ESP8266+阿里云物联网平台:从设备创建到双向通信的保姆级配置指南
  • 2026年Q355B钢管好用的厂家推荐 - mypinpai
  • 答辩PPT制作效率翻倍!百考通AI学术PPT实战测评
  • 【第 4 篇:RAG 知识库问答——检索只是第一步】
  • 算盘科技深度解析:定制智慧城市解决方案的顶层设计“珠算”逻辑
  • Linux视频教程之高级运维企业实战(高级版)【共24课时】_Linux课程-51CTO学堂
  • 手把手教你用VMware虚拟机搭建Linux版DNF私服(附一键安装包下载)
  • 从沐神的‘动手学深度学习’到Kaggle提交:一个数据科学新人的完整复盘与避坑指南
  • ALTER TABLE:MySQL 增强表结构的最佳实践与避坑指南
  • 如何用qmc-decoder轻松解密QQ音乐加密音频文件?
  • 3步搞定:抖音无水印下载工具高效解决方案