协程怎么理解
- 一种在程序中处理并发任务的方案;也是该方案的一个组件
- 协程和线程属于一个层级的概念
- 协程中不存在线程,也不存在并行(并行不是并发)
协程的好处
- 处理耗时任务,这种任务时常会堵塞主线程
- 保证主线程安全,即确保安全地从主线程调用任何suspend函数
- 协程让异步逻辑同步化,杜绝回调地狱
- 协程最核心的点就是,函数或者一段程序能够挂起,稍后再在挂起的位置恢复
下面是关于协程这个概念的一些描述
协程的开发人员 Roman Elizarov 是这样描述协程的:协程就像非常轻量级的线程。线程是由系统调度
的,线程切换或线程阻塞的开销都比较大。而协程依赖于线程,但是协程挂起时不需要阻塞线程,几乎是无
代价的,协程是由开发者控制的。所以协程也像用户态的线程,非常轻量级,一个线程中可以创建任意个协
程。
Coroutine,翻译成”协程“,初始碰到的人马上就会跟进程和线程两个概念联系起来。直接先说区别,
Coroutine是编译器级的,Process和Thread是操作系统级的。Coroutine的实现,通常是对某个语言
做相应的提议,然后通过后成编译器标准,然后编译器厂商来实现该机制。Process和Thread看起来也在
语言层次,但是内生原理却是操作系统先有这个东西,然后通过一定的API暴露给用户使用,两者在这里有
不同。Process和Thread是os通过调度算法,保存当前的上下文,然后从上次暂停的地方再次开始计算,
重新开始的地方不可预期,每次CPU计算的指令数量和代码跑过的CPU时间是相关的,跑到os分配的cpu时
间到达后就会被os强制挂起。Coroutine是编译器的魔术,通过插入相关的代码使得代码段能够实现分段
式的执行,重新开始的地方是yield关键字指定的,一次一定会跑到一个yield对应的地方
对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继
续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。协程能保留上一次调用时的状
态,不需要像线程一样用回调函数,所以性能上会有提升。缺点是本质是个单线程,不能利用到单个CPU的
多个核
扔物线表述
对某些语言,比如Kotlin,这样说是没有问题的,Kotlin的协程库可以指定协程运行的线程池,我们只
需要操作协程,必要的线程切换操作交给库,从这个角度来说,协程就是一个线程框架。
但理论上我们可以在单线程语言如JavaScript、Python上实现协程(事实上他们已经实现了协程),这时
我们再叫它线程框架可能就不合适了。
协程依赖
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0")
启动
1.协程需要运行在有协程上下文环境,在非协程环境凭空启动协程,有三种方式
- GlobalScope.launch{}
在应用范围内启动一个协程,协程的生命周期与应用程序一致。这样启动的协程不能使线程保活,就像守护线程
由于这样启动的协程在启动协程的组件已被销毁但协程还存在的情况,可能导致资源耗尽,因此不推荐这样启动,尤其是在客户端这种需要频繁创建销毁组件的场景
- 实现CoroutineScope + launch{}
这是在应用场景最推荐使用协程的方式,为自己的组件实现CoroutieScope接口,在需要的地方使用launch{}方法启动协程。使得协程和该组件生命周期绑定,组件销毁时,协程一并销毁。从而实现安全可靠的协程调用
- runBlocking{}
启动一个新协程,并阻塞当前线程,直到内部逻辑以及子线程逻辑全部执行完成。
该方法的设计目的是让suspend在编写库中能够再常规堵塞代码中使用,常在main方法和测试中使用
2.在一个协程中启动子协程,一般来说有两种
- launch{}
异步启动一个字写成
- async{}
异步启动一个子协程,并返回Deffer对象,可通过调用Deffer.await()方法等待该子协程执行完成并获取结果,常用于并发-同步等待的情况
协程的挂起与恢复
- 常规的函数基础操作包括,invoke(或Call)和return,协程新增了suspend和resume
1.suspend 也称为挂起或者暂停,用于暂停执行当前协程,并保存所有局部变量(挂起点保存了)
2.resume 用于让已暂停的协程从其暂停处继续执行
- 挂起函数
1.使用suspend关键字修饰的函数叫做挂起函数
2.挂起函数只能在协程体内或者其他挂起函数内调用
调度器
- 所有的协程都必须在调度器中运行,即使他们在主线程上也是如此
如果协程没有指定调度器的话,默认在dispatchers.Default运行
任务泄露
- 当某个协程任务丢失,无法追踪,导致内存、CPU、磁盘等资源浪费,甚至发送一个无用的网络请求,这种情况叫做任务泄露
- 为了能够避免协程泄露,Kotlin引入了结构化并发机制
结构化并发
- 取消任务,当某项任务不再需要时取消它
- 追踪任务,当任务正在执行时,追踪它
- 发出错误信号,当协程失败时,发出错误信号表明有错误发生