Kotlin-协程原理(3)- 协程启动

104 阅读3分钟

juejin.cn/editor/draf…, 上文分析了协程创建的过程

本文重点分析协程的启动

分析重点:我们知道LaunchUnderTheHoodKttestLaunchtestLaunch1表示协程体。那么启动协程,也就是触发invokeSuspend函数,所以只要关注底层如何触发invokeSuspend函数这个重点就可以了,有这个前提才就不至于在源码中迷失。

  1. startCoroutineCancellable 负责创建协程、封装协程、启动协程
public fun <T> (suspend () -> T).startCoroutineCancellable(completion: Continuation<T>): Unit = runSafely(completion) {
    createCoroutineUnintercepted(completion).intercepted().resumeCancellableWith(Result.success(Unit))//1
}

//上面的intercepted触发这里
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =
    (this as? ContinuationImpl)?.intercepted() ?: this


//ContinuationImpl.intercepted() 上面的intercepted触发这里
public fun intercepted(): Continuation<Any?> =
    intercepted
        ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
            .also { intercepted = it }//2

  • 注释1:上文分析得知道,startCoroutineCancellable是协程创建、启动的核心,createCoroutineUnintercepted(completion)是负责协程的创建,返回协程体对象 LaunchUnderTheHoodKttestLaunchtestLaunch1,它间接继承BaseContinuationImpl对象;intercepted是负责协程的包装;resumeCancellableWith负责协程的启动;
  • 注释2:context[ContinuationInterceptor]获取协程的Dispatchers,这里是Dispatchers.Default;
  1. intercepted包装协程
//CoroutineDispatcher类中
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
    DispatchedContinuation(this, continuation)
  • Dispatchers.Default的父类是DispatchedContinuation对象,this是Dispatchers.Default本身,continuation是协程体本身;这里是将Dispatchers跟协程体包装起来;
  1. 开始启动协程
internal class DispatchedContinuation<in T>(
    @JvmField val dispatcher: CoroutineDispatcher,//就是Dispatchers.Default
    @JvmField val continuation: Continuation<T>// 就是协程体本身
) : DispatchedTask<T>(MODE_UNINITIALIZED), CoroutineStackFrame, Continuation<T> by continuation {
   //省略部分代码
    inline fun resumeCancellableWith(
        result: Result<T>,
        noinline onCancellation: ((cause: Throwable) -> Unit)?
    ) {
        val state = result.toState(onCancellation)
        if (dispatcher.isDispatchNeeded(context)) {//1
            _state = state
            resumeMode = MODE_CANCELLABLE
            dispatcher.dispatch(context, this)/2
        } else {
            //省略部分代码
        }
    }
     //省略部分代码
}
  • 注释1:只有Dispatchers.Unconfined isDispatchNeeded才返回false,所以走if分支,这里把this传入,this实现了Runnable接口
  1. Dispatchers分发任务
//ExperimentalCoroutineDispatcher类中
override fun dispatch(context: CoroutineContext, block: Runnable): Unit =
    coroutineScheduler.dispatch(block)//1
  • 注释1:Dispatchers.Default的父类是ExperimentalCoroutineDispatcher,注意第二个参数block,它包含协程体本身;coroutineScheduler才是核心,是CoroutineScheduler类型;
  1. 调度器分发
internal class CoroutineScheduler(
    @JvmField val corePoolSize: Int,
    @JvmField val maxPoolSize: Int,
    @JvmField val idleWorkerKeepAliveNs: Long = IDLE_WORKER_KEEP_ALIVE_NS,
    @JvmField val schedulerName: String = DEFAULT_SCHEDULER_NAME
) : Executor, Closeable {//1
    //省略部分代码
    fun dispatch(block: Runnable, taskContext: TaskContext =      NonBlockingContext, tailDispatch: Boolean = false) {
        val task = createTask(block, taskContext)//2
        val  = currentWorker()//3
        val notAdded = currentWorker.submitToLocalQueue(task, tailDispatch)、、4
         //省略部分代码
     }
}
  • 注释1:CoroutineScheduler实现Executor,而且注意看属性corePoolSize、maxPoolSize,是不是看到了线程池的影子;
  • 注释2:返回TaskImpl任务,持有协程体block
  • 注释3:获取当前Work,也就是线程,尝试将协程添加到线程的本地队列等待调度;
加入本地队列以后,如何被调度呢?
  1. 核心在Worker.runWorker
internal inner class Worker private constructor() : Thread() {
    //省略部分代码
    private fun runWorker() {
        var rescanned = false
        while (!isTerminated && state != WorkerState.TERMINATED) {
            val task = findTask(mayHaveLocalTasks)//1
            if (task != null) {
                executeTask(task)//2
                continue
            } 
            //省略部分代码
    }
    
    private fun executeTask(task: Task) {
        //省略部分代码
        runSafely(task)
        //省略部分代码
    }
     
    fun runSafely(task: Task) {
        task.run()//3
        //省略部分代码
    }

  • 注释1:查找要执行的任务;
  • 注释2:调用executeTask执行,最后触发task.run(),Task的实现是TaskImpl;
  • 注释3:触发TaskImpl的run方法;
internal class TaskImpl(
    @JvmField val block: Runnable,
    submissionTime: Long,
    taskContext: TaskContext
) : Task(submissionTime, taskContext) {
    override fun run() {
        try {
            block.run()
        } finally {
            taskContext.afterTask()
        }
    }
//省略部分代码
  • 执行构造TaskImpl对象时,传入的block,它包含持有协程体
  1. 从前面的分析得知,block是DispatchedContinuation类型,run方法的实现定义在它的父类DispatchedTask类中
internal abstract class DispatchedTask<in T>(
    @JvmField public var resumeMode: Int
) : SchedulerTask() {
    //省略部分代码
    public final override fun run() {
         //省略部分代码
        try {
            val delegate = delegate as DispatchedContinuation<T>
            val continuation = delegate.continuation
            withContinuationContext(continuation, delegate.countOrElement) {
               //省略部分代码
                val job = if (exception == null && resumeMode.isCancellableMode) context[Job] else null
                if (job != null && !job.isActive) {
                    val cause = job.getCancellationException()
                    cancelCompletedResult(state, cause)
                    continuation.resumeWithStackTrace(cause)
                } else {
                    if (exception != null) {
                        continuation.resumeWithException(exception)
                    } else {
                        continuation.resume(getSuccessfulResult(state))//3
                    }
                }
            }
        } catch (e: Throwable) {
           //省略部分代码
    }
  • 判断是否有异常、协程是否存活等,都通过了,最后执行continuation.resume;还记得构建DispatchedContinuation对象时的第二个参数吗?所以这里的continuation是协程体本身
  1. 协程体间接继承BaseContinuationImpl,所以会触发BaseContinuationImpl.resumeWith
internal abstract class BaseContinuationImpl(
    public val completion: Continuation<Any?>?
) : Continuation<Any?>, CoroutineStackFrame, Serializable {
    //省略部分代码
    public final override fun resumeWith(result: Result<Any?>) {
            //省略部分代码
            with(current) {
                    try {
                        val outcome = invokeSuspend(param)//1
                        if (outcome === COROUTINE_SUSPENDED) return
                        Result.success(outcome)
                    } catch (exception: Throwable) {
                        Result.failure(exception)
                    }
                    //省略部分代码
            }
        }
    }
  • 注释1,最后触发协程体实现的invokeSuspend的方法,欧耶,协程体启动了。
总结

协程的启动也就是LaunchUnderTheHoodKttestLaunchtestLaunch1 invokeSuspend函数被触发;

以上分析有不对的地方,请指出,互相学习,谢谢哦!