前面两篇文章里面介绍了协程的一些原理与概念,所谓知其然更要知其所以然,本篇文章我们将正式从协程的创建开始研究协程的源码。
,Kotlin 并没有给我暴露给用户可以直接创建协程的类,上一边文章中我们介绍过CoroutineScope 可以看成的协程建造者,是协程框架对外暴露的创建协程的接口。
fun testCoroutine(){
GlobalScope.launch {
println("我是testCoroutine 之前")
}
}
上面我们通过GlobalScope.launch 创建一个极为简单的协程,这个协程会运行在一个新的线程里面,然后打印一句话就结束了。
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
我们来不解释每行代码含义:
1 第一个参数context 实际是一个Map数据结构,至于为什么Koltin要定义这样一个数据结构而不是直接使用现有的Map,暂时没有想通,这里的作用是给新创建的协程传递一些参数,例如最典型的Dispatchers.IO,表示新的协程运行在IO线程池内
2 start 表示启动的方式,常用的是DEFAULT与Lazy 两种,DEFAULT表示创建一个新的协程并立即运行,Lazy 表示创建之后但是不运行,需要用户调用start方法之后再运行,就像Java new了一个Thread 但是在没有调用thread的start方法之前是不会运行的。
3 block 是一个suspend 方法,关于suspend 我们在上一篇文章介绍过,所有suspend修饰的方法在编译之后都会生成一个对应的Function2类以及一个协程续体类(我们会在后面在具体讲解这个类)。
suspend CoroutineScope.() -> Unit 表示调用block时候需要传递一个CoroutineScope的参数。同样我们在前面讲解过所有的suspend修饰的方法在编译之后都会多出一个Continuation类型的参数,所以实际上在调用block 方法的时候我们需要传递两个参数,这是跟普通的方法不一样的地方。这里说的细致一点以来避免有些同学对Koltin语法不熟悉,另一方面提前给大家解释下,传递的两个参数会在后面遇到。
public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
、//kotlin重定义了加法符号,这里可以理解成map.addAll
val combined = coroutineContext + context
val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combined
//
return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
debug + Dispatchers.Default else debug
}
newCoroutineContext 方法的主要作用是设置新创建的协程运行在哪一个线程池里面。
用户有两种方式设置协程的Dispatcher 也就是决定协程运行的线程池,第一种是自定义CoroutineScope 重写coroutineContext ,往内部添加上想要的Dispatcher. 第二种是在launch的时候传递一个Dispatcher,若是两个都没有指定的话,那么框架会指定一个默认的是Dispatcher ,这实际也就是newCoroutineContext 的作用。
首先GlobalScope 的coroutineContext 是EmptyCoroutineContext 而且我们在launch的时候也没传递参数,所以这里val combined = coroutineContext + context 结果依然是EmptyCoroutineContext,那么下面的条件自然就是成立的,此时就会添加一个默认的Dispatcher
//launch的时候没有传递start所以这里start 为默认值DEFAULT
coroutine.start(start, coroutine, block)
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
//这个是建立父子协程之间的关系
initParentJob()
//注意这里不是递归调用,start指的是CoroutineStart
start(block, receiver, this)
}
initParentJob 主要是建立父子协程之间额关系,我们这里创建的第一个协程,其没有父协程所以这里不再分析,会留在后面的父子协程再来分析这个方法,这里提示一下start(block, receiver, this) 不是递归调用,当初看到这里我迷糊了好一阵子。
实际start(block, receiver, this) 是执行的下面invoke这个方法,由于我们的start类型是DEFAULT 所以后面会走第一个分支。
/**
* Use this function to start coroutine in a cancellable way, so that it can be cancelled
* while waiting to be dispatched.
*/
internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(receiver: R, completion: Continuation<T>) =
runSafely(completion) {
createCoroutineUnintercepted(receiver, completion).intercepted().resumeCancellable(Unit)
}
private inline fun runSafely(completion: Continuation<*>, block: () -> Unit) {
try {
block()
} catch (e: Throwable) {
completion.resumeWith(Result.failure(e))
}
}
completion 表示一个协程续体,在这里表示的我们当前创建的协程StandaloneCoroutine。
runSafely 表示的是当block执行出现异常,通知协程处理异常,放到这里来讲 completion.resumeWith(Result.failure(e)) 会结束当前协程,关于协程的状态与异常处理我们放到后面细讲。我们这里主要分析block,
block代码块我们这里分为三个部分来,第一createCoroutineUnintercepted 第二intercepted 第三resumeCancellable
createCoroutineUnintercepted
public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
receiver: R,
completion: Continuation<T>
): Continuation<Unit> {
//probeCoroutineCreated 什么也没做直接返回completion
val probeCompletion = probeCoroutineCreated(completion)
return if (this is BaseContinuationImpl)
create(receiver, probeCompletion)
else {
//会走这个分支
createCoroutineFromSuspendFunction(probeCompletion) {
//每一个协程续体都实现了Function2接口,这里调用invoke 就是创建协程续体
(this as Function2<R, Continuation<T>, Any?>).invoke(receiver, it)
}
}
}
这个方法位于IntrinsicsJvm文件内,大家查看源码的时候注意一下,别找错了位置。
这个方法相当的重要,这个方法的主要作用是创建一个协程续体,我们之前说过suspend 修饰的方法会在编译的过程中生成一个对应的协程续体类,这里创建的协程续体就是Global.launch最后那个参数对应的续体。
就这个例子而言,就是launch后面的大括号里面的代码会被编译成一个协程续体类,而createCoroutineUnintercepted 方法就是创建这个协程续体并且执行这个协程续体。
private inline fun <T> createCoroutineFromSuspendFunction(
completion: Continuation<T>,
crossinline block: (Continuation<T>) -> Any?
): Continuation<Unit> {
val context = completion.context
// label == 0 when coroutine is not started yet (initially) or label == 1 when it was
return if (context === EmptyCoroutineContext)
object : RestrictedContinuationImpl(completion as Continuation<Any?>) {
}
else
//执行当前这个分支
//ContinuationImpl 也是一个协程续体,而且是协程的第一个续体,
object : ContinuationImpl(completion as Continuation<Any?>, context) {
private var label = 0
override fun invokeSuspend(result: Result<Any?>): Any? =
//一个简单的状态机,这个状态机我们放到下一篇文章讲解。
when (label) {
0 -> {
label = 1
result.getOrThrow()
//执行block,block内部会创建我们传入代码块对应的协程续体
block(this)
}
1 -> {
//
label = 2
result.getOrThrow() // this is the result if the block had suspended
}
else -> error("This coroutine had already completed")
}
}
}
当然block 代码块不会立即执行,而是执行完后面的两个方法,将任务抛到线程之后之后才会运行invokeSuspend 然后才会执行block代码块,不过我们提前这这里解释这句代码的意思
(this as Function2<R, Continuation<T>, Any?>).invoke(receiver, it)
this 这里指的是(suspend R.() -> T)方法,我们前面说过,每一个suspend 方法在编译期间会生成一个对应的Function2类,这里在编译之后this指代的就是这个编译期间生成的Function2类,Function2有名字就知道需要两个参数,(suspend R.() -> T这里为
suspend CoroutineScope.() -> Unit,所以第一个参数就是CoroutineScope类型,实际就是前面创建的StandaloneCoroutine,第二个参数it实际也是StandaloneCoroutine,这里表示的父续体或者理解成父协程也可以。
生成的这个Function2类型的类主要作用就是创建对应的续体,后续由这个续体来控制生成的协程的挂起与恢复。
intercepted
@SinceKotlin("1.3")
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =
(this as? ContinuationImpl)?.intercepted() ?: this
createCoroutineUnintercepted 返回的是ContinuationImpl,所以这里进入到ContinuationImpl的intercepted
public fun intercepted(): Continuation<Any?> =
intercepted
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
.also { intercepted = it }
context[ContinuationInterceptor]?.interceptContinuation(this) 这句代码的意思是获取协程的分发器也就是Dispatcher,大家可以看看CoroutineDispatcher的继承结构
这里有一个奇怪的语法大家需要注意AbstractCoroutineContextElement 的构造方法需要的是一个Key但是实际上我们第一张图上面传入的确是ContinuationInterceptor,你要说这是一个class 他应该写成ContinuationInterceptor::class 这样才对,你要说他是Key吧ContinuationInterceptor还没有继承Key这个类,实际上这里指的就是第三章截图里面伴随变量Key,大家当成一个语法糖理解就行,当初看到这的时候把我迷糊了老一会。
我们接着向下分析,获获取到Dispatcher 之后会调用其interceptContinuation,这方法定义在CoroutineDispatcher内
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
DispatchedContinuation(this, continuation)
resumeCancellable
DispatchedContinuation 也是一个协程续体,
inline fun resumeCancellable(value: T) {
//isDispatchNeeded 这里返回true
if (dispatcher.isDispatchNeeded(context)) {
_state = value
resumeMode = MODE_CANCELLABLE
dispatcher.dispatch(context, this)
} else {
executeUnconfined(value, MODE_CANCELLABLE) {
if (!resumeCancelled()) {
resumeUndispatched(value)
}
}
}
}
这里的dispatcher 是Dispacthers.Default,而Dispacthers.Default根据不同情况会使用DefaultScheduler或者CommonPool,这里以CommonPool 为例分析,因为CommonPool更简单
@Volatile
private var pool: Executor? = null
override fun dispatch(context: CoroutineContext, block: Runnable) {
try {
//将任务放到加入到线程池执行
(pool ?: getOrCreatePoolSync()).execute(timeSource.wrapTask(block))
} catch (e: RejectedExecutionException) {
timeSource.unTrackTask()
DefaultExecutor.enqueue(block)
}
}
可以看到这里就死将block 加入到一个线程池里面等待执行,而block 这个对象定义在DispatchedContinuation 的父类DispatchedTask 里面
public final override fun run() {
val taskContext = this.taskContext
var exception: Throwable? = null
try {
//delegate 指的是DispatchedContinuation
val delegate = delegate as DispatchedContinuation<T>
//continuation 是我们前面创建的ContinuationImpl
val continuation = delegate.continuation
val context = continuation.context
val job = if (resumeMode.isCancellableMode) context[Job] else null
val state = takeState() // NOTE: Must take state in any case, even if cancelled
withCoroutineContext(context, delegate.countOrElement) {
if (job != null && !job.isActive) {
//这里表示协程取消执行
val cause = job.getCancellationException()
cancelResult(state, cause)
continuation.resumeWithStackTrace(cause)
} else {
val exception = getExceptionalResult(state)
if (exception != null)
continuation.resumeWithStackTrace(exception)
else
//第一次执行会走这里
continuation.resume(getSuccessfulResult(state))
}
}
} catch (e: Throwable) {
// This instead of runCatching to have nicer stacktrace and debug experience
exception = e
} finally {
val result = runCatching { taskContext.afterTask() }
handleFatalException(exception, result.exceptionOrNull())
}
}
public inline fun <T> Continuation<T>.resume(value: T): Unit =
resumeWith(Result.success(value))
下面就会进入到至关重要的ContinuationImpl的resumeWith ,这个方法在ContinuationImpl的父类BaseContinuationImpl
internal abstract class BaseContinuationImpl(
// This is `public val` so that it is private on JVM and cannot be modified by untrusted code, yet
// it has a public getter (since even untrusted code is allowed to inspect its call stack).
public val completion: Continuation<Any?>?
) : Continuation<Any?>, CoroutineStackFrame, Serializable {
// This implementation is final. This fact is used to unroll resumeWith recursion.
public final override fun resumeWith(result: Result<Any?>) {
// This loop unrolls recursion in current.resumeWith(param) to make saner and shorter stack traces on resume
var current = this
var param = result
while (true) {
//probeCoroutineResumed 什么也没有做
probeCoroutineResumed(current)
with(current) {
val completion = completion!! // fail fast when trying to resume continuation without completion
val outcome: Result<Any?> =
try {
//invokeSuspend 会调用到我们传入的代码块
val outcome = invokeSuspend(param)
//COROUTINE_SUSPENDED 表示协程挂起,当一个协程挂起需要等待另一个
//协程再度调用这个协程的rusumeWith 方法来恢复执行
if (outcome === COROUTINE_SUSPENDED) return
Result.success(outcome)
} catch (exception: Throwable) {
Result.failure(exception)
}
releaseIntercepted() // this state machine instance is terminating
if (completion is BaseContinuationImpl) {
// unrolling recursion via loop
//子协程续体执行完了,将执行结果传递给父续体
//
current = completion
param = outcome
} else {
// top-level completion reached -- invoke and return
//执行到这个分支的话,表示协程执行完了,
//这个completion 就是我们上面创建的StandaloneCoroutine
completion.resumeWith(outcome)
return
}
}
}
}
}
上面的 invokeSuspend 方法如下,block(this) 方法上面介绍过了。
我们这里对resumeWith 只是稍微讲解了一些,更多具体的内容我们放在后面解释,至此关于协程的创建流程就算是讲完了,下面一张要介绍的就是协程的状态机,这一部分需要结合反编译的代码来理解。