当前位置: 首页 > 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.jsqmd.com/news/939478/

相关文章:

  • MySQL外键约束详解
  • 辛格迪丨药企计算机化系统合规升级:全生命周期管控筑牢监管核查防线
  • 从Proteus仿真到PCB打样:一个51单片机电压表的完整开发实战(附ADC0809调试心得)
  • 答辩PPT高效制作技巧:百考通AI助力在校生告别排版内耗
  • [分享]InputBridge 手机玩PC游戏神器!虚拟键盘
  • 为什么你的音乐收藏总缺歌词?163MusicLyrics如何解决这个痛点
  • 利用快马平台与oh-my-opencode快速构建可配置的web应用原型
  • WinForm桌面程序里直接跑Unity3D场景,C#和Unity实时互传数据
  • 告别Spine?在Unity中低成本玩转DragonBones龙骨动画的完整配置与性能小贴士
  • MySQL 分区表进阶:分区策略选型 + 分区维护 + 性能对比(实战避坑)
  • 01-Playwright 浏览器与上下文
  • AI 中转站关停风波:灰色生意背后藏法律风险,合规出口待开启
  • 手把手解决Python 4大高频报错!新手90%都踩过
  • 华为交换机LACP配置避坑指南:eNSP实验里那些容易忽略的细节(接口优先级、抢占延迟实战解析)
  • 避坑指南:在Ubuntu 20.04上从零搭建DAVE与UUV_Simulator水下仿真环境(含CUDA配置与常见报错解决)
  • OpenCV C++圆检测增强模块:多圆稳定识别+抗干扰优化
  • 深入Linux内核:Livepatch如何实现函数“热替换”而不宕机?
  • 从CANoe到实车:UDS Flash刷写全流程自动化测试搭建指南(Python/ CAPL脚本)
  • 如何精准下载GitHub文件和目录:DownGit完整解决方案
  • 计算机毕业设计之资讯求真平台的设计与实现
  • MySQL索引优化宝典:10个案例教你分析慢SQL,让查询速度提升100倍
  • 从MySQL分库分表到OceanBase分区:实战迁移中的那些坑与最佳实践
  • 深度解析开源项目:京东智能评价自动化解决方案完全指南
  • bug描述规范
  • 训练1个电影级AI视频模型要多少算力?独家披露Netflix/腾讯影业联合实验室的3.7PB数据集构建逻辑与轻量化部署路径
  • 白盒测试——动态测试——逻辑覆盖法
  • ChatGPT Windows客户端下载与技术架构深度解析(Electron+Vite+React)
  • 5分钟告别混乱:用Ice重新定义你的macOS菜单栏体验
  • GBase 8c逻辑解码解析
  • ai-agent 响应速度优化