0x0、引言
Kotlin 1.3 版本开始引入协程 Coroutine
,简练的官方文档 和 网上一堆浅尝辄止的文章让我心里没底,不想止步于仅仅知道:
① Android中,Kotlin协程用于解决:
处理耗时任务
和保证主线程安全
;② 利用Kotlin协程,可以用看起来:
同步
的方式编写异步
代码;③ 基础的API调用;
我还想了解更多,如协程的概念,Kotlin协程在实际开发中的使用,背后的原理等,遂有此文。
Kotlin协程的源码还没啃完,此系列目前只能算是笔记,边看边学,部分内容摘取自参考文献,只是让潜意识里有这些概念,后续看完源码,缕清思路再来整理一波,有纰漏之处,欢迎评论区友善指出,一起讨论学习,谢谢~
本文主要阐述一些概念相关的东西,一些前置知识,有所了解得可直接跳过。
0x1、追根溯源
1、同步 & 异步
先撇开编程相关的东西不说,通过 坐公交
的形象化例子帮助理解 同步与异步:
乘客排队等公交,车来了,前门扫码上车,一个扫完到下一个扫,一种
串行化
的关系,这是同步
;
前门乘客上车,后门乘客下车,互不影响,同时进行,一种
并行化
的关系,这是异步
。
我们把乘客上车和下车,看做是两个 任务
,司机开车也是一个任务,跟这两个任务是异步关系。异步说明两者可以同时进行,乘客还没上完车,司机直接把车开走,也是可以的:
不过这显然不合常理,正常来说:司机应该等乘客上下车完毕才发车,那司机怎么知道:
常规操作有两种:
轮询(主动):每隔一段时间查看下前后门监控,看下还有没有乘客;
回调(被动):早期的公交车上都会配有一个乘车员,没乘客上下车了,她就会喊司机开车;
2、堵塞 & 非堵塞
同步和异步的关注点是 是否同时进行
,而堵塞和非堵塞关注的是 能否继续进行
,还是坐公交的例子:
有乘客上下车,司机发车就需要
等待
,此时司机发车的任务处于堵塞
状态;
乘客都上下车完毕,司机又可以发车了,此时司机发车的任务处于
非堵塞
状态;
堵塞的真正含义:关心的事物由于某些原因,无法继续进行,因此让你等待。
等待:只是堵塞的一个副作用,表明随时间流逝,没有任何有意义的事物发生或进行。
堵塞时,没必要干等着,可以做点其他无关的事物,因为这不影响你对相关事情的等待;
比如司机等发车时,可以喝喝茶、看看手机等,但不能离开。
计算机没人那么灵活,堵塞时干等最容易实现,只需挂起线程,让出CPU即可,等条件满足时,在重新调度此线程。
3、程序
回到编程相关,任务
对应计算机中的 程序
,定义如下:
为了完成特定任务,用某种编程语言编写的一组指令集合(一组 静态代码)
CPU处理器 会 逐条执行指令,哪怕出现外部中断,也只是从当前程序切到另一段程序,继续逐条执行。
和预期一致,代码 逐条执行,但有些业务场景 顺序结构 就无能为力了,比如:
女朋友:你下班后去超市买10个鸡蛋回来,看到有卖西瓜的就买1个
此时,需要用到四种 基础控制流 中的另外一种 → 选择执行:
剩下两种基础控制流为 迭代和递归,我们使用 控制流
来完成 逻辑流
,程序执行到哪,逻辑就执行到哪,这样的程序结构清晰,可读性好,比较符合编程人员的思维习惯,这也是 同步编程
的方式。
4、进程
同一时刻只有一个程序在内存中被CPU调用运行
假设有A、B两个程序,A正在运行,此时需要读取大量输入数据(IO操作),那么CPU只能干等,直到A数据读取完毕,再继续往下执行,A执行完,再去执行程序B,白白浪费CPU资源。
看着有点蠢,能不能这样:
当程序A读取数据的时,切换 到程序B去执行,当A读取完数据,让程序B暂停,切换 回程序A执行?
当然可以,不过在计算机里 切换
这个名词被细分为两种状态:
挂起
:保存程序的当前状态,暂停当前程序;
激活
:恢复程序状态,继续执行程序;
这种切换,涉及到了 程序状态的保存和恢复,而且程序A和B所需的系统资源(内存、硬盘等)是不一样的,那还需要一个东西来记录程序A和B各自需要什么资源,还有系统控制程序A和B切换,要一个标志来识别等等,所以就有了一个叫 进程的抽象
。
进程的定义
程序在一个数据集上的一次动态执行过程,一般由下述三个部分组成:
- 程序:描述进程要完成的功能及如何完成;
- 数据集:程序在执行过程中所需的资源;
- 进程控制块:记录进程的外部特征,描述执行变化过程,系统利用它来控制、管理进程,系统感知进程存在的唯一标志。
进程是系统进行 资源分配和调度 的一个 独立单位。
进程的出现使得多个程序得以 并发
执行,提高了系统效率及资源利用率,但存在下述问题:
① 单个进程只能干一件事,进程中的代码依旧是串行执行。
② 执行过程如果堵塞,整个进程就会挂起,即使进程中某些工作不依赖于正在等待的资源,也不会执行。
③ 多个进程间的内存无法共享,进程间通讯比较麻烦。
于是划分粒度更小的 线程
出现了。
5、线程
线程的出现是为了降低上下文切换消耗,提高系统的并发性,并突破一个进程只能干一件事的缺陷,使得 进程内并发 成为可能。
线程的定义
轻量级的进程,基本的CPU执行单元,亦是 程序执行过程中的最小单元,由 线程ID、程序计数器、寄存器组合和堆栈 共同组成。线程的引入减小了程序并发执行时的开销,提高了操作系统的并发性能。
区分:「进程」是「资源分配」的最小单位,「线程」是 「CPU调度」的最小单位
线程和进程的关系
① 一个程序至少有一个进程,一个进程至少有一个线程,可以把进程理解做
线程的容器
;② 进程在执行过程中拥有
独立的内存单元
,该进程里的多个线程共享内存
;③ 进程可以拓展到
多机
,线程最多适合多核
;④ 每个独立线程有一个程序运行的入口、顺序执行列和程序出口,但不能独立运行,需依存于应用程序中,由应用程序提供多个线程执行控制;
进程和线程都是一个时间段的描述,是 CPU工作时间段的描述,只是颗粒大小不同。