Tips 0:写完全文回头看,我觉得只看总结也够了。可以快进。
前言
最初接触 Kotlin 的时候,Kotlin 协程还处于实验阶段,我也是第一次听说协程的概念。几年过去了,随着 Kotlin 的推广和 Kotlin 协程的发展,我终于在去年年底正式入坑了。
大约5个月的时间,从 demo 用到真实项目,我对 Kotlin 协程的看法也一波三折,从不明觉厉到不过如此,又逐渐变成这东西真不错。
关于 Kotlin 协程我有很多想写的内容,草稿箱和笔记里都有不同的提纲,但第一篇就还是从困扰了我最久的点开始讲起吧,那就是「协程」的概念和 Kotlin 中的协程。
本文中协程和 Kotlin 协程是两种并列的不同的概念,二者没有包含关系。
从协程的概念开始
Tips 1:对于还未接触过协程的人来说,我觉得学习 Kotlin 协程完全不需要去深入理解协程本身的概念,因为 Kotlin 协程并不是真正的协程,早期过度研究概念和对比区别是学习 Kotlin 协程的一条弯路。
根据维基百科的介绍,协程并不是新技术,这个概念正式发布于1963年,是一种实现多任务并发的技术思路,大概与「线程」位于同一级别,二者的区别则是线程采取「抢占式」策略,而协程采取「协作式」策略。
单从概念上看,协程可控性比线程高,似乎是更优解,但协作式的方案不能实现真正的并行,很显然,线程取得了胜利。
「协程」一度沉寂,但随着软件开发的复杂度提升,很多语言都重新捡起了协程的概念来处理异步操作,但具体实现上已经与曾经的协程相去甚远。
Kotlin 协程的目标
Kotlin 协程是一个 JetBrains 官方出品的库,但不包含在 Kotlin 语言标准库中,目标是解决开发者用不好线程的问题(我编的)。
根据官方介绍,Kotlin 协程是用来实现异步任务和非阻塞编程的。以 Android 开发为例,为了保证良好的用户体验,系统规定 UI 线程不可阻塞,以便立刻响应用户输入,所有耗时操作都应该在其他线程实现,再通知 UI 线程更新页面。不只是 Android,所有对外提供服务的程序都有这样的需求,给了 Kotlin 协程更广泛的发挥空间。
Kotlin 协程的功能
Kotlin 的协程使用了协程的概念,通过「挂起」和「恢复」简化了多任务协作的实现代码。Kotlin 协程是一种对同步编程和异步编程统一的抽象封装。
- 挂起和恢复
挂起和恢复来自协程的基本概念,每个协程自身决定何时挂起何时继续,达成多任务之间协作。
在一次次调用 suspend 函数的过程中,表面上连续的调用栈实际上已经完成了线程切换。
被挂起和恢复的对象是协程,也就是说在第一次 suspend 之后线程 A 并未被阻塞,还可以执行其他发生在线程 A 的代码。
- 多任务
任务是一种抽象概念,可以理解成一段代码或者一个函数,执行任务需要一定的时间,任务可能正常完成获得需要的结果,也可能遇到异常而失败。Kotlin 协程的协作是多任务之间的协作,任务通常在不同的线程,但也可以在相同的线程。(一个挂起函数内也可以调用普通函数)
- 关于简化
只看官方对 Kotlin 协程的介绍可能觉得简化的作用很大,其实那些例子都没有处理 Exception,实际应用协程的时候并不会那么简单。但简化代码并不是虚假宣传,实际上简化的是恢复的过程,协程恢复的过程是将其他任务的处理结果传回来的地方,如果使用回调的话,免不了加一层嵌套,任务越多越复杂。Kotlin 协程的简化是把这些嵌套拉平了。
一段总结
写完之后我又反复读了文章内容,不断修改,但还是觉得漏洞百出。讲述概念的每一处用词都经过了反复推敲,但还是会发现前后矛盾、内容重复、歧义等问题。有点只可意会不可言传的意思了。
总结一下观点吧:
- 当对比协程和线程时,通常是指概念上的协程,与 Kotlin 协程不是同一个东西
- Kotlin 协程是一个用协程概念在线程框架上封装的库
- Kotlin 协程可以解决异步代码难写难读的问题,但并非不可替代的黑科技
投降了,讲概念太可怕了,下一篇写 Kotlin 协程和 ViewModel、Retrofit、Room 的实践。
参考资料:
Tips 2: 《深入理解 Kotlin 协程》这本书通过横向对比不同语言的协程实现,或者不同异步任务实现方案阐述了 Kotlin 协程的各方面特点,也有对源码的一些讲解,重理论轻实践,最好在有一定的实践经验之后再看。