协程是如何启动的?

简介: 协程是如何启动的?

1. 前言


本文专注于讲解MainScope启动协程的整体流程。由于时间和篇幅关系,对于文中的一些前置知识点不会过多讲解。希望本文能给读者对于协程的启动有个基本的了解。要想真正理解启动原理,还需要亲力亲为,跟着文章Debug一番。


2. 简单的例子


首先 我们来看一个简单的例子,在主线程中开启一个协程,打印“Hello Coroutines”。通过这个简单的例子讲解协程是如何启动起来的。

640.png

接着我们将startCoroutines方法反编译成Java文件

640.png


我们看到Kotlin代码中的Lambda表达式变成了Java中的Function2对象,而它对应的具体类是SuspendLambda。我们注意到它实现了三个方法:


  1. Object invokeSuspend(Object var1)
  2. Continuation create(Object value,Continuation completion)
  3. Object invoke(Object var1,Object var2)


640.jpg

640.png

640.png


大家仔细看就会发现,invokeSuspend和create方法定义在BaseContinuationImpl中,invoke方法定义Function2中。而SuspendLambda继承了ContinuationImpl同时又实现了SuspendFunction接口。


在Kotlin中,方法也是对象,一个参数的方法对应的类是Function1,以此类推两个参数方法对应的是Function2。


行文至此,有很多新东西,suspend、Continuation、CoroutineScope、CoroutineContext,但是本文不会讲解,接着往下看协程启动过程吧。

3. 启动过程

3.1 CoroutineScope.launch


640.png


该方法功能如下:


  1. 为新协程创建一个新的CoroutineContext
  2. 本文Case创建StandaloneCoroutine
  3. 调用coroutine.start方法启动协程


640.png


该方法功能如下:


  1. 调用initParentJob方法,与父Job建立关联,当调用cancel方法或者子Job有异常时,可以将取消或者异常事件往上传播(本文暂且忽略,简单了解就好了
  2. 调用start(block, receiver, this),该方法超级有迷惑性,它真正调用的是CoroutineStart的invoke方法。


3.2 CoroutineStart.invoke

640.png


本案例中执行DEFAULT分支


3.3 (suspend(R)->T).startCoroutineCancellable

640.png

最终是通过suspend方法的startCoroutineCancellable方法来启动协程的,本文对应的是lambda,前文我们讲过它对应的是SuspendLambda


{
  println("Hello Coroutines")
}

该方法包含了几个重要方法的调用:


  1. (suspend R.()->T).createCoroutineUnintercepted
  2. Continuation.intercepted()
  3. Continuation.resumeCancellableWith


3.4 (suspend R.() -> T).createCoroutineUnintercepted

640.png


当前的this是启动协程的闭包,前面我们通过反编译startCoroutines方法,发现闭包实现了create方法,此处调用的正是反编译后生成的create方法

640.png

参数completion对应的是协程StandaloneCoroutine,此处生成的Function2对象也是SuspendLambda对象


3.5 Continuation.intercepted()

640.png

该方法的主要作用就是生成DispatchedContinuation。它由CoroutineDispatcher和Continuation两部分组成。Dispatcher决定在Continuation在哪个线程中执行,本文是在主线程


3.6 Continuation.resumeCancellableWith

640.png

3.7 MainScope

640.png

Dispatchers.Main 最终由kotlinx.coroutines.android.AndroidDispatcherFactory创建


3.8 AndroidDispatcherFactory

640.png

最终通过Handler.post方法把DispatchedContinuation放入主线程消息队列


3.9 DispatchedTask.run


Android主线程调度,最终会调用到DispatchedTask.run方法中,通过continuation.resume方法执行协程体。

640.png


3.10 BaseContinuationImpl.resumeWith

ContinuationImpl.kt

640.png

该方法循环调用Continuation的invokeSuspend方法,直到当前completion是协程本身会跳出循环。


真正执行打印“Hello Coroutines”

640.png



3.11 AbstractCoroutine.resumeWith


协程执行完成


640.png


表示协程执行结束,它可能需要等待子协程结束,结束后需要告知父协程


4. 总结


协程的前置知识点太多了,一篇文章无法解释清楚,有疑问可以在评论区提出。本文主要是讲解了Dispatchers.Main启动协程的过程。Dispatchers.Default启动协程的过程与它又完全不同。

相关文章
|
5月前
|
前端开发 程序员 调度
探索协程在 C++ 中的实现方式
探索协程在 C++ 中的实现方式
154 2
|
5月前
|
Unix Linux 编译器
进程、线程、协程的区别
进程、线程、协程的区别
50 0
|
Go 调度
永远不要在不知道如何停止的情况下启动一个 goroutine
永远不要在不知道如何停止的情况下启动一个 goroutine
68 0
|
存储 并行计算 安全
线程,进程,协程
线程,进程,协程
98 0
|
安全 Linux API
进程、线程、协程
面试官:简单聊一下进程、线程、协程的区别吧。。。
250 0
进程、线程、协程
|
消息中间件 Unix 程序员
python中强制关闭线程、协程、进程方法
python中强制关闭线程、协程、进程方法
537 0
python中强制关闭线程、协程、进程方法
|
安全 Go 调度
go一个协程安全协程调度的问题
go一个协程安全协程调度的问题
114 0
go一个协程安全协程调度的问题
【多线程】线程是如何启动的?
大家好,今天开始我们来聊一聊多线程。通过这样子的方式来督促自己对知识系统的学习,也希望能给大家一些帮助。
|
安全 程序员 调度
Goroutine 是什么?进程、线程、协程又是什么?有什么区别和联系?
进程,直观点说,保存在硬盘上的程序运行之后,会在内存空间里形成一个独立的内存体,这个内存体有自己独立的地址空间,有自己的堆,上级挂靠单位是操作系统。
170 0
Goroutine 是什么?进程、线程、协程又是什么?有什么区别和联系?
|
存储 安全 Java
【HotspotJVM后台运行的系统线程】
【HotspotJVM后台运行的系统线程】
143 0