协程的上下文和调度
协程的调度
协程拥有多种调度器,使用不用的协程调度器,协程会运行在不同的线程中
Unconfined是在当前默认的协程中运行,但是在遇到第一个暂停点之后回复的线程是不确定的,所以对于Unconfined,无法保证全都在当前线程中调用
父子协程
通过一个协程的coroutineContext
(当前协程的上下文)来启动另一个协程,新协程的job就会称为该协程的孩子
由于父协程的取消,会导致子协程也会被取消,而job2是job的孩子,job1不是,所以job2只输出了一句话
多个CoroutineContext进行+
操作
协程支持多个CoroutineContext进行+
操作,使得一个协程具有多个CoroutineContext的特性
上图使用了+coroutineContext之后,childJob就变成job的子协程了,job取消后它也跟着取消。CoroutineContext能够使用+操作是因为它重载了运算符
CoroutineContext+Job
如果CoroutineContext与job
对象相加,job对象可以直接管理该协程,job取消任务后,后续的协程就不再运行了
协程的作用域CoroutineScope
协程可以通过创建一个CorotineScope对象来创建,之前的launch,async都是CoroutineScope的扩展函数。GlobalScope
是CoroutineScope的实现类。GlobalScope没有绑定任何job对象,它用于构建最顶层的协程,这些协程的生命周期会跟随着整个Application
尽量少用GlobalScope
在GlobalScope中创建的Coroutines可能会导致应用崩溃
GlobalScope的性质
- 全局CoroutineScope不受任何限制。
- 全局范围用于启动在整个应用程序生命周期内运行且不会过早取消的顶级协程。 全局范围的另一种用法是在Dispatchers.Unconfined中运行的运算符,它们没有任何关联的工作。
- 应用程序代码通常应使用应用程序定义的CoroutineScope。不建议在GlobalScope实例上使用异步或启动。
安全使用CoroutineScope
在android的Jetpack组件中ViewModel、Lifecycle也有其自身对应的CoroutinesScope,方便在其生命周期的活动状态使用协程,比如viewModelScope
、lifecycleCoroutineScope
Channel机制
生产者和消费者
下图两个协程,一个作为生产者发送信息,另一个作为消费者消费信息,通过channel
建立关系
而且channel可以关闭
此时代码会报异常,因为三次之后channel关闭,消费者协程无法消费信息
管道
在类UNIX操作系统中,管道是一系列将标准输入输出连接起来的进程,其中每一个进程的输出被直接作为下一个进程的输入。每一个链接都由匿名管道实现,管道中的组成元素也被称为过滤程序。channel参考这个概念设计了Pipelines
的功能
produce
函数是Coroutine builder,能够方便地构造协程和发送信息,produce返回一个ReceiveChannel
对象。consumeEach
是ReceiveChannel的扩展函数,用于消费者循环地消费信息。
channel缓冲
channel和produce函数的创建都能够指定创建缓冲区的大小
这里创建的channel缓冲区的大小是2,前面两个放在了缓冲区,尝试发送第三个的时候就挂起了
第一个协程先发送两个消息,需要等到第二个协程delay时间到了,第二个协程才开始消费信息。等到缓冲区的消息消费完毕后,第一个协程继续发送信息,第二个携程继续消费信息,直到结束
actor
actor本身就是一个协程,内部包含一个channel
,通过channel与其他协程进行通信。要注意actor内部是从channel接收信息的
Select
表达式
Select表达式能够同时等待多个suspending function,然后选择第一个可用的结果