协程是如何启动的?

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

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启动协程的过程与它又完全不同。

相关文章
|
8月前
|
前端开发 程序员 调度
探索协程在 C++ 中的实现方式
探索协程在 C++ 中的实现方式
204 2
|
8月前
|
Unix Linux 编译器
进程、线程、协程的区别
进程、线程、协程的区别
93 0
|
Go 调度
永远不要在不知道如何停止的情况下启动一个 goroutine
永远不要在不知道如何停止的情况下启动一个 goroutine
80 0
【C++11】C++多线程之条件变量,异步启动任务(1)
【C++11】C++多线程之条件变量,异步启动任务(1)
145 0
|
安全 调度 开发者
并发异步编程之争:协程(asyncio)到底需不需要加锁?(线程/协程安全/挂起/主动切换)Python3
协程与线程向来焦孟不离,但事实上是,线程更被我们所熟知,在Python编程领域,单核同时间内只能有一个线程运行,这并不是什么缺陷,这实际上是符合客观逻辑的,单核处理器本来就没法同时处理两件事情,要同时进行多件事情本来就需要正在运行的让出处理器,然后才能去处理另一件事情,左手画方右手画圆在现实中本来就不成立,只不过这个让出的过程是线程调度器主动抢占的。
并发异步编程之争:协程(asyncio)到底需不需要加锁?(线程/协程安全/挂起/主动切换)Python3
|
消息中间件 Unix 程序员
python中强制关闭线程、协程、进程方法
python中强制关闭线程、协程、进程方法
568 0
python中强制关闭线程、协程、进程方法
【多线程】线程是如何启动的?
大家好,今天开始我们来聊一聊多线程。通过这样子的方式来督促自己对知识系统的学习,也希望能给大家一些帮助。
多线程详解p10、线程停止
多线程详解p10、线程停止
多线程详解p10、线程停止
Java多线程:线程的创建与启动
Java多线程:线程的创建与启动
Java多线程:线程的创建与启动
Java多线程:如何停止/中断一个运行中的线程?
Java多线程:如何停止/中断一个运行中的线程?
Java多线程:如何停止/中断一个运行中的线程?