进程
早期的计算机运行程序还是只能一次运行一个任务,之后进程的出现实现了近似同步的执行效果,其本质上是程序的交替执行。为了保证进程中的程序能够正常执行,还会有一些存储进程状态的保存集。随着硬件的发展和多CPU的出现,能够同时执行的进程数量逐渐增多。这就带来了一个问题,即用来存储进程状态的集合所占用的资源比一个进程可以执行的资源还要多,相当于整个系统大半的进行都是用来保存进程的状态。
线程
线程的提出有效的解决了这个问题。进程不再频繁的切换,而是先执行,遇到阻塞的话暂时不管,继续执行其他的任务,当其他任务执行完之后再回过头来看阻塞任务是否执行完。多线程的缺陷在于无法自主控制调度,除开一定会执行的主线程之外,其他线程的执行顺序都无法控制,在Java上是由Java虚拟机调度,其他平台大多是由系统控制。
协程
线程执行过程中发生线程切换的时候会损耗一定的资源,这部分资源用来保存线程的状态。执行过程中如果发生了磁盘读写或网络请求这样的IO操作的时候线程的执行会被阻塞,但同时该线程还会持有CPU资源,这就造成了一定了资源浪费。理想的情况是在发送阻塞的时候,该线程主动交出CPU给其他线程使用或者给内部的其他任务。
这种方式其实就是协程的体系。通过提升CPU利用率,减少线程切换,进而提升程序运行效率。
延伸开来协程主要有三个特性:
第一个是可控制,不同于线程协程能做到可被控制的发起子任务;
第二个是轻量级,协程非常小、占用资源比线程还少,在JVM平台上它的本质就是一次方法的调用;
第三个是语法糖,目前能够使用协程的语言都提供了很好的语法糖支持,使多任务或多线程切换不在使用回调语法。
协程很好的解决了两个问题
耗时任务
主线程安全
android 主线程负责UI和用户交互,如果主线程任务过多就会阻塞主线程。为了在主线程避免网络请求采用CallBack回调,
- suspend —— 挂起当前协程的执行,保存所有局部变量
- resume —— 从被挂起协程挂起的地方继续执行
Kotlin协程通过挂起和恢复共同工作来替代回调。
Kotlin中每个协程的协程体都实现了这个接口Continuation,协程的本质也就是一次回调,只不过通过语法糖的形式让它看起来像是顺序执行
Kotlin 提供了 Dispatchers 来处理线程调度
通过协程,你可以细粒度的控制线程调度。 withContext
让你可以控制任意一行代码运行在什么线程上,而不用引入回调来获取结果
为了避免泄露协程,Kotlin 引入了 structured concurrency(结构化并发)。结构化并集合了语言特性和最佳实践,遵循这个原则将帮助你追踪协程中的所有任务。
在 Android 中,我们使用结构化并发可以做三件事:
- 取消不再需要的任务
- 追踪所有正在进行的任务
- 协程失败时的错误信号
让我们深入探讨这几点,来看看结构化并发是如何帮助我们避免丢失对协程的追踪以及任务泄露