Kotlin 协程完全解读三

前面两篇文章里面介绍了协程的一些原理与概念,所谓知其然更要知其所以然,本篇文章我们将正式从协程的创建开始研究协程的源码。

,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 只是稍微讲解了一些,更多具体的内容我们放在后面解释,至此关于协程的创建流程就算是讲完了,下面一张要介绍的就是协程的状态机,这一部分需要结合反编译的代码来理解。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值