浅析kotlin协程launch 、delay调用

4,728 阅读7分钟

一、简单的协程例子

image.png 这个main方法直接看成程序入口方法,此方法不带suspend修饰。

二、runBlocking 启动一个协程

runBlocking一般不在开发中使用,在测试框架中用到,连接非协程世界到协程世界,runBlocking的代码块都在coroutineScope的作用域内。
上面的main方法开启了两个协程,父协程是runBlocking启动的,子协程是里面launch启动的。父协程要等所有的子协程完成才能结束。
先看下runBlocking的源码:

public fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T {
    .....
    val currentThread = Thread.currentThread()   //肯定是主线程
    val contextInterceptor = context[ContinuationInterceptor]  //拦截器,为null
    val eventLoop: EventLoop?        //事件轮询 handler那一套???
    val newContext: CoroutineContext
    if (contextInterceptor == null) {
        eventLoop = ThreadLocalEventLoop.eventLoop  
        newContext = GlobalScope.newCoroutineContext(context + eventLoop)
    } else {
        eventLoop = (contextInterceptor as? EventLoop)?.takeIf { it.shouldBeProcessedFromContext() }
            ?: ThreadLocalEventLoop.currentOrNull()
        newContext = GlobalScope.newCoroutineContext(context)
    }
    val coroutine = BlockingCoroutine<T>(newContext, currentThread, eventLoop) 
    coroutine.start(CoroutineStart.DEFAULT, coroutine, block)  //start=create+ resumeWith(Unit)
    return coroutine.joinBlocking()
}

启动协程时runBlocking在这里阻塞了主线程。coroutine的启动使用start,其实start= create + resumeWith(Unit).,EventLoop参考熟悉的handler的流程,肯定存在事件队列,插入事件,取事件的操作,如事件未达处理时间就"阻塞"。

三、launch 启动一个子协程

能够启动协程的方式除了launch,还有async+await,其中async返回的是一个Deferred。

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
}
public interface Deferred<out T> : Job   //Deferred也是个Job

luanch的返回值是个Job,通过(coroutine.start)启动协程,在这里就作为子协程,都说子协程会继承的父协程的上下文,这是真的吗?真的。

@ExperimentalCoroutinesApi
public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
    val combined = coroutineContext + context     #1
    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方法的作用域也是最外层coroutineScope,然后它的上下文是#1中的coroutineContext,很明显,子协程它可以有自己的上下文,两上下文可以进行combined,这里没有的添加调度器Dispatchers.Default,所以线程也还是主线程。coroutineContext + context 上下文能相加,用的就是重载plus操作符

//from  CoroutineContext 
public operator fun plus(context: CoroutineContext): CoroutineContext =
   ......
       }

CoroutineContext还支持coroutineContext[Job],就用到了get约定,重载get运算符。

//from  CoroutineContext
public operator fun <E : Element> get(key: Key<E>): E?

coroutineContext[Job]中的key是伴生对象,既然是伴生对象,那就是个final修饰的静态对象,协程上下文类的多个对象可以共享这个key,值会不会覆盖呢?

public interface ContinuationInterceptor : CoroutineContext.Element {
    companion object Key : CoroutineContext.Key<ContinuationInterceptor>

协程调度器、协程拦截器、协程拦截器都是协程上下文,都实现了CoroutineContext.Element。

image.png

3.1、从协程编译后的产物发现它的本体

在runBlocking中启动两个子协程,子协程共用一个线程。👇

image.png

使用show kotlin bytecode转换后的代码👇

public final class TestKt {
    public static final void main() {
        BuildersKt.runBlocking((CoroutineContext)(new CoroutineName("coroutine#1")), (Function2)(new Function2((Continuation)null) {
            int label;
            @Nullable
            public final Object invokeSuspend(@NotNull Object var1) {
                Object var6 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
                switch(this.label) {
                    case 0:
                        ResultKt.throwOnFailure(var1);
                        System.out.println("1  " + Thread.currentThread().name + "---" + coroutineContext[CoroutineName])
                        ExecutorCoroutineDispatcher dispatcher = ThreadPoolDispatcherKt.newSingleThreadContext("my owner Thread ");
                        BuildersKt.launch$default($this$runBlocking, (new CoroutineName("coroutine#2")).plus((CoroutineContext)dispatcher), (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
                            int label;
                            @Nullable
                            public final Object invokeSuspend(@NotNull Object $result) {
                                Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
                                switch(this.label) {
                                    case 0:
                                        ResultKt.throwOnFailure($result);
                                        System.out.println("2  " + Thread.currentThread().name + "---" + coroutineContext[CoroutineName])
                                        this.label = 1;
                                        if (DelayKt.delay(1000L, this) == var5) {
                                            return var5;
                                        }
                                        break;
                                    case 1:
                                        ResultKt.throwOnFailure($result);
                                        break;
                                    default:
                                        throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
                                }
                                System.out.println("3  " + Thread.currentThread().name + "---" + coroutineContext[CoroutineName])
                                return Unit.INSTANCE;
                            }

                            @NotNull
                            public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
                                Intrinsics.checkNotNullParameter(completion, "completion");
                                Function2 var3 = new <anonymous constructor>(completion);
                                var3.L$0 = value;
                                return var3;
                            }

                            public final Object invoke(Object var1, Object var2) {
                                return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
                            }
                        }), 2, (Object)null);
                        BuildersKt.launch$default($this$runBlocking, (new CoroutineName("coroutine#2")).plus((CoroutineContext)dispatcher), (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
                            int label;
                            @Nullable
                            public final Object invokeSuspend(@NotNull Object $result) {
                                Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
                                switch(this.label) {
                                    case 0:
                                        ResultKt.throwOnFailure($result);
                                        System.out.println("21  " + Thread.currentThread().name + "---" + coroutineContext[CoroutineName])
                                        this.label = 1;
                                        if (DelayKt.delay(500L, this) == var5) {
                                            return var5;
                                        }
                                        break;
                                    case 1:
                                        ResultKt.throwOnFailure($result);
                                        break;
                                    default:
                                        throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
                                }
                                System.out.println("31  " + Thread.currentThread().name + "---" + coroutineContext[CoroutineName])
                                return Unit.INSTANCE;
                            }

                            @NotNull
                            public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
                                Intrinsics.checkNotNullParameter(completion, "completion");
                                Function2 var3 = new <anonymous constructor>(completion);
                                var3.L$0 = value;
                                return var3;
                            }

                            public final Object invoke(Object var1, Object var2) {
                                return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
                            }
                        }), 2, (Object)null);
                        System.out.println("4  " + Thread.currentThread().name + "---" + coroutineContext[CoroutineName])
                        return Unit.INSTANCE;
                    default:
                        throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
                }
            }

            @NotNull
            public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
                Intrinsics.checkNotNullParameter(completion, "completion");
                Function2 var3 = new <anonymous constructor>(completion);
                var3.L$0 = value;
                return var3;
            }

            public final Object invoke(Object var1, Object var2) {
                return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
            }
        }));
    }
    public static void main(String[] var0) {
        main();
    }
}

想不到协程的bytecode也会存在回调嵌套,这里还只是父协程嵌套两个子协程,如果存在多个协程嵌套,层级多的话,那将也存在回调地狱。
协程能够挂起的本质,其实就是协程体的代码被挂起点一分为二,这里delay是挂起函数,那代码就分为协程体开始到挂起点delay,delay后面到结束,前面的代码走switch的case 0的逻辑,后面的代码就是挂起点恢复之后,再次走下case 1以及后面的代码。

注意调用挂起函数,不一定会真正挂起,具体看返回值是不是IntrinsicsKt.getCOROUTINE_SUSPENDED();,是的就直接return了,下次需再次调用invokeSuspend,就会走label==1了,执行后面的逻辑。
单拎出一个子协程看看:

 BuildersKt.launch$default($this$runBlocking, (new CoroutineName("coroutine#2")).plus((CoroutineContext)dispatcher), (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
    int label;
    @Nullable
    public final Object invokeSuspend(@NotNull Object $result) {   #2 这个是能找到的
        Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
        switch(this.label) {
            case 0:
                ResultKt.throwOnFailure($result);
                System.out.println("2  " + Thread.currentThread().name + "---" + coroutineContext[CoroutineName])
                this.label = 1;
                if (DelayKt.delay(1000L, this) == var5) {
                    return var5;
                }
                break;
            case 1:
                ResultKt.throwOnFailure($result);
                break;
            default:
                throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
        }
        System.out.println("3  " + Thread.currentThread().name + "---" + coroutineContext[CoroutineName])
        return Unit.INSTANCE;
    }

    @NotNull
    public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
        Intrinsics.checkNotNullParameter(completion, "completion");
        Function2 var3 = new <anonymous constructor>(completion);
        var3.L$0 = value;
        return var3;
    }

    public final Object invoke(Object var1, Object var2) {  #1 这里的调用没有找到
        return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
    }
}), 2, (Object)null);

首先invoke() #1是怎么触发的?没有找到对应的调用地方,有小伙伴找到的话,麻烦告知下。
再次调用invokeSuspend #2是怎么触发的? 是在continuation.resumeWith(xxx)时触发。

众所周知,kotlin的suspend方法编译之后会在最后一个参数的位置放上Continuation<T>

public fun CoroutineScope.launch(context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit): Job 
BuildersKt.launch$default($this$runBlocking, 
(new CoroutineName("coroutine#2")).plus((CoroutineContext)dispatcher), 
(CoroutineStart)null, (Function2)(new Function2((Continuation)null) {}),2, (Object)null);

对应看下,CoroutineScope的launch拓展函数,在java调用时就是作为第一个参数传入,第二个参数就是协程上下文,第三个参数是协程启动模式,接下来为一个就是Function2的匿名类对象,接收参数是Continuation类型,最后多出的两个参数是2和(Object)null,这两个参数是啥,暂不清楚。

当查看原始字节码时,可以看到以下声明:

final class com/docwei/kotlindemo/TestKt$main$1$1
extends kotlin/coroutines/jvm/internal/SuspendLambda    //协程体编译成了SuspendLambda类!!!!
implements kotlin/jvm/functions/Function2 {             //同时也是Fucntion2!!!

INVOKESTATIC kotlinx/coroutines/BuildersKt.launch$default 
(Lkotlinx/coroutines/CoroutineScope;
Lkotlin/coroutines/CoroutineContext;
Lkotlinx/coroutines/CoroutineStart;
Lkotlin/jvm/functions/Function2;
ILjava/lang/Object;)Lkotlinx/coroutines/Job;

SuspendLambda类 可以看下类继承关系

image.png

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) //#1
    return coroutine
}

#1看起来是我们创建了一个新协程,然后传入block就是我们的协程体。

//from AbstractCoroutine
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
    start(block, receiver, this)
}
//from CoroutineStart
public operator fun <T> invoke(block: suspend () -> T, completion: Continuation<T>): Unit =
    when (this) {
        DEFAULT -> block.startCoroutineCancellable(completion) //#2 会走这里的
        ATOMIC -> block.startCoroutine(completion)
        UNDISPATCHED -> block.startCoroutineUndispatched(completion)
        LAZY -> Unit // will start lazily
    }

#2看这里的block的声明,带suspend的lambda,去启动支持取消的协程。

internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(
    receiver: R, completion: Continuation<T>,
    onCancellation: ((cause: Throwable) -> Unit)? = null
) =
    runSafely(completion) {   // create + resumeWith = start
        createCoroutineUnintercepted(receiver, completion).intercepted().resumeCancellableWith(Result.success(Unit), onCancellation)
    }
private inline fun runSafely(completion: Continuation<*>, block: () -> Unit) {
    try {
        block()
    } catch (e: Throwable) {
        dispatcherFailure(completion, e)
    }
}

一路跟下来,你会发现,这个R,其实就是CoroutineScope,completion就是我们coroutine.start(start, coroutine, block)中的coroutine,它同时也是个Continuation(表示协程其实就是一个Continuation,也是个CoroutineScope| 协程体是一个SuspendLambda,它也是Continuation),看下从AbstractCoroutine的类声明可以看出:

public abstract class AbstractCoroutine<in T>(
    parentContext: CoroutineContext,
    initParentJob: Boolean,
    active: Boolean
) : JobSupport(active), Job, Continuation<T>, CoroutineScope {

block: suspend CoroutineScope.() -> Unit会编译成SuspendLambda类

//from  Intrinsicskt
public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
    receiver: R,
    completion: Continuation<T>
): Continuation<Unit> {
    val probeCompletion = probeCoroutineCreated(completion)
    return if (this is BaseContinuationImpl)
        create(receiver, probeCompletion)  //其实就是创建suspendLambda对象
    else {
        ......
    }
}
//from ContinuationImpl
public fun intercepted(): Continuation<Any?> =
    intercepted
        ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
            .also { intercepted = it }
            

记住协程启动时的start= create + resumewith(Unit)

@InternalCoroutinesApi
public fun <T> Continuation<T>.resumeCancellableWith(
    result: Result<T>,
    onCancellation: ((cause: Throwable) -> Unit)? = null
): Unit = when (this) {
    is DispatchedContinuation -> resumeCancellableWith(result, onCancellation)
    else -> resumeWith(result)
}
inline fun resumeCancellableWith(result: Result<T>,
    noinline onCancellation: ((cause: Throwable) -> Unit)?
) {
    val state = result.toState(onCancellation)
    if (dispatcher.isDispatchNeeded(context)) {
        _state = state
        resumeMode = MODE_CANCELLABLE
        dispatcher.dispatch(context, this) //
    } else {
        .....
    }
}

image.png 我们传入的调度器是一个自定义的调度器,那这里就是在启动协程时就会调度一下(其实就是切换下线程),让子协程的协程体在这个线程里执行。

//from ExecutorCoroutineDispatcherImpl
override fun dispatch(context: CoroutineContext, block: Runnable) {
    try {
        executor.execute(wrapTask(block)) 
    } catch (e: RejectedExecutionException) {
        unTrackTask()
        cancelJobOnRejection(context, e)
        Dispatchers.IO.dispatch(context, block)
    }
}

线程池执行任务,那肯定看其run方法了。

//from DispatchedTask
public final override fun run() {
    try {
            ......
            continuation.resume(getSuccessfulResult(state))
        }
    } catch (e: Throwable) {
      ......
    } finally {
        ......
    }
}
//from BaseContinuationImpl
public final override fun resumeWith(result: Result<Any?>) {
        .....
        val outcome = invokeSuspend(param)  //invokeSuspend终于调用了
        if (outcome === COROUTINE_SUSPENDED) return
        Result.success(outcome)
       .......
    }
}

所以,Function2的invokeSuspend第一次调用是在协程启动时的resumeWith(Unit)触发。

3.2、 自定义单线程池newSingleThreadContext(xxx)

val dispatcher = newSingleThreadContext("my owner Thread ")

public fun newSingleThreadContext(name: String): ExecutorCoroutineDispatcher =
    newFixedThreadPoolContext(1, name)
    
public fun newFixedThreadPoolContext(nThreads: Int, name: String): ExecutorCoroutineDispatcher {
    require(nThreads >= 1) { "Expected at least one thread, but $nThreads specified" }
    val threadNo = AtomicInteger()
    val executor = Executors.newScheduledThreadPool(nThreads) { runnable ->
        val t = Thread(runnable, if (nThreads == 1) name else name + "-" + threadNo.incrementAndGet())
        t.isDaemon = true  //守护线程
        t
    }
    return executor.asCoroutineDispatcher()
}   

通过newSingleThreadContext创建的线程,是个守护线程,难道是防止协程未能正常取消时,app退出了,也能终止执行?而且 很关键的点就是Executors.newScheduledThreadPool通过这种方式生成的线程池,方便做定时任务,比如delay操作。

3.3、 协程的调度线程情况

image.png launch启动的线程还是主线程,那先整个默认调度器 image.png 启动调度其实很简单,就是将协程体直接封装成runnable,丢给线程池去执行,就是所谓的调度了。 delay的挂起恢复,只要当前线程池支持定时执行,就用当前线程,不支持的话,就用一个默认的DefaultExecutor的来处理,当然DefaultExecutor它不是ScheduledExecutorService,其通过LockSupport.parkNanos(blocker, nanos) 实现。
下面看看DefaultExecutor的守护线程run方法👇:

//Thread的run方法,然后这个线程还是个守护线程。
override fun run() {
    ThreadLocalEventLoop.setEventLoop(this)
    registerTimeLoopThread()
    try {
        var shutdownNanos = Long.MAX_VALUE
        if (!notifyStartup()) return
        while (true) {   //死循环
            Thread.interrupted() // just reset interruption flag 
            var parkNanos = processNextEvent()  //取下一个事件
            if (parkNanos == Long.MAX_VALUE) {
                // nothing to do, initialize shutdown timeout
                val now = nanoTime()
                if (shutdownNanos == Long.MAX_VALUE) shutdownNanos = now + KEEP_ALIVE_NANOS
                val tillShutdown = shutdownNanos - now
                if (tillShutdown <= 0) return // shut thread down
                parkNanos = parkNanos.coerceAtMost(tillShutdown)
            } else
                shutdownNanos = Long.MAX_VALUE
            if (parkNanos > 0) {
                if (isShutdownRequested) return
                parkNanos(this, parkNanos)    //发现未达到处理时间,就阻塞这个守护线程。
            }
        }
    } finally {
        _thread = null // this thread is dead
        acknowledgeShutdownIfNeeded()
        unregisterTimeLoopThread()
        if (!isEmpty) thread // recreate thread if it is needed
    }
}
@InlineOnly
internal inline fun parkNanos(blocker: Any, nanos: Long) {
    timeSource?.parkNanos(blocker, nanos) ?: LockSupport.parkNanos(blocker, nanos)
}

来复习下handler的Looper.loop,阻塞通过epoll_wait来实现,对照看看。

public static void loop() {
        for (;;) {
             Message msg = queue.next(); // might block
             ......
              msg.target.dispatchMessage(msg);
             ......
        }
}
Message next() {
        int nextPollTimeoutMillis = 0;
        for (;;) {
            nativePollOnce(ptr, nextPollTimeoutMillis); //底层最后调用到epoll_wait
            synchronized (this) {
                ......
                if (msg != null) {
                    if (now < msg.when) {
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                       ......
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }
             ......
            nextPollTimeoutMillis = 0;
        }
    }

三、delay(time)

如果把协程跟线程类比,delay(1000)看成线程的sleep(1000),sleep会阻塞线程,但是协程里面的delay会阻塞当前线程吗?不会。继续上面的例子,让他们在同一个线程上调度,然后都delay。你会发现它并没有阻塞这个线程。

image.png

delay并不会阻塞当前协程的执行线程,所以被称为轻量级。那delay既然不阻塞当前线程,那它怎么知道要在指定时间恢复呢?莫非用了跟android类似的handler的postdelay的机制,当然不是。

public suspend fun delay(timeMillis: Long) {
    if (timeMillis <= 0) return // don't delay
    return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
        if (timeMillis < Long.MAX_VALUE) {
            cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
        }
    }
}
public suspend inline fun <T> suspendCancellableCoroutine(
    crossinline block: (CancellableContinuation<T>) -> Unit
): T =suspendCoroutineUninterceptedOrReturn { uCont ->
        val cancellable = CancellableContinuationImpl(uCont.intercepted(), resumeMode = MODE_CANCELLABLE)
        cancellable.initCancellability() 
        block(cancellable)
        cancellable.getResult()
    }
internal class ExecutorCoroutineDispatcherImpl(override val executor: Executor) : ExecutorCoroutineDispatcher(), Delay {

将continuation封装成支持取消的CancellableContinuation,cancellable.initCancellability() 这里父子协程建立关联,是不是封装成了支持取消的协程,就进入了状态机流程了?

//from ExecutorCoroutineDispatcherImpl
override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
    val future = (executor as? ScheduledExecutorService)?.scheduleBlock(
        ResumeUndispatchedRunnable(this, continuation),
        continuation.context,
        timeMillis
    )
    if (future != null) {  //如果当前调度的线程池是个ScheduledExecutorService就直接在当前线程池处理
        continuation.cancelFutureOnCancellation(future)
        return
    }//不是就用个默认的DefaultExecutor处理
    DefaultExecutor.scheduleResumeAfterDelay(timeMillis, continuation)
}
private fun ScheduledExecutorService.scheduleBlock(block: Runnable, context: CoroutineContext, timeMillis: Long): ScheduledFuture<*>? {
    return try {
        schedule(block, timeMillis, TimeUnit.MILLISECONDS)
    } catch (e: RejectedExecutionException) {
        cancelJobOnRejection(context, e)
        null
    }
}
//ScheduledExecutorService
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);

这里delay的实现是通过ScheduledExecutorService的scheule方法实现,肯定不会阻塞当前线程,这个子协程调度的线程的线程池是一个支持定时执行任务的线程池,这点要跟走默认的DefaultExecutor的LockSupport区分开。
runnable的run方法是dispatcher.resumeUndispatched(Unit)。

private class ResumeUndispatchedRunnable(
    private val dispatcher: CoroutineDispatcher,
    private val continuation: CancellableContinuation<Unit>
) : Runnable {
    override fun run() {
       //这里分发的是个Unit
        with(continuation) { dispatcher.resumeUndispatched(Unit) }
    }
}
override fun CoroutineDispatcher.resumeUndispatched(value: T) {
    val dc = delegate as? DispatchedContinuation
    resumeImpl(value, if (dc?.dispatcher === this) MODE_UNDISPATCHED else resumeMode)
}
//协程的状态机处理状态扭转
private fun resumeImpl(
    proposedUpdate: Any?,
    resumeMode: Int,
    onCancellation: ((cause: Throwable) -> Unit)? = null
) {
    _state.loop { state ->
        when (state) {
            is NotCompleted -> {
                val update = resumedState(state, proposedUpdate, resumeMode, onCancellation, idempotent = null)
                if (!_state.compareAndSet(state, update)) return@loop // retry on cas failure
                detachChildIfNonResuable()
                dispatchResume(resumeMode) // dispatch resume, but it might get cancelled in process
                return // done
            }
            is CancelledContinuation -> {
           ......
}

协程的状态机就开始处理状态流转,状态机这块跳过。

private fun dispatchResume(mode: Int) {
    if (tryResume()) return // completed before getResult invocation -- bail out
    dispatch(mode)
}

image.png

private fun dispatchResume(mode: Int) {
    if (tryResume()) return // completed before getResult invocation -- bail out
    dispatch(mode)
}
internal fun <T> DispatchedTask<T>.dispatch(mode: Int) {
  .......
    } else {
        resume(delegate, undispatched)
    }
}
internal fun <T> DispatchedTask<T>.resume(delegate: Continuation<T>, undispatched: Boolean) {
    // This resume is never cancellable. The result is always delivered to delegate continuation.
    val state = takeState()
    val exception = getExceptionalResult(state)
    val result = if (exception != null) Result.failure(exception) else Result.success(getSuccessfulResult<T>(state))
    when {
        undispatched -> (delegate as DispatchedContinuation).resumeUndispatchedWith(result)
        else -> delegate.resumeWith(result)
    }
}
@Suppress("NOTHING_TO_INLINE") // we need it inline to save us an entry on the stack
inline fun resumeUndispatchedWith(result: Result<T>) {
    withContinuationContext(continuation, countOrElement) {
        continuation.resumeWith(result)
    }
}

当看到这个continuation.resumeWith(result),肯定再次回调invokeSuspend,代码就往下继续执行。

//from BaseContinuationImpl
public final override fun resumeWith(result: Result<Any?>) {
        .....
        val outcome = invokeSuspend(param)  //invokeSuspend终于调用了
        if (outcome === COROUTINE_SUSPENDED) return
        Result.success(outcome)
       .......
    }
}

那现在可以明白,如果一个协程里面,有个delay,那么resumeWith(Unit)会调用两次,一次调用是启动协程时调用,一次是delay恢复时调用。(两次resumeWith(Unit)调用就对应java字节码中的invokeSuspend方法的两次调用)。
bennyhuo说:协程中如果有n个挂起点,那总共调用n+2次resume(2来自启动时调用一次,结束时一次) 结束时也会调用resumeWith?貌似不会,resumeWith启动调用一次,结束再次调用的话,invokeSuspend就会调用3次,label等于1的case的逻辑就会走多次,那应该打印多次,然而并没有。

四、后续

目前状态机这块没有碰,还有协程的事件处理机制也是,他们都可以单独拎出来解析看看。