Coroutine挂起与恢复分析

从编译代码看挂起

用Kotlin简单写一个Coroutine的小例子,代码如下:

@InternalCoroutinesApi
fun main() {
    println("main")
    runBlocking {
        println("main launch")
        test1()
    }
}

suspend fun test1() {
    println("test1")
    delay(100)
    println("test1-end")
}

以上两个函数一个有suspend关键字修饰,一个没有,起对应编译后代码也有所不同,首先看一个main()函数编译后的源码:

   public static final void main() {
      String var0 = "main";
      boolean var1 = false;
      System.out.println(var0);
      //Builders.kt中没有default()函数,该处调用逻辑不清楚,但是不影响主流程分析
      BuildersKt.runBlocking$default((CoroutineContext)null, (Function2)(new Function2((Continuation)null) {
         private CoroutineScope p$;
         Object L$0;
         int label;

         @Nullable
         public final Object invokeSuspend(@NotNull Object $result) {
            //COROUTINE_SUSPENDED标志标示函数挂起,在源码中会判断该标志
            Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
            CoroutineScope $this$runBlocking;
            switch(this.label) {
            case 0:
               ResultKt.throwOnFailure($result);
               $this$runBlocking = this.p$;
               String var3 = "main launch";
               boolean var4 = false;
               System.out.println(var3);
               this.L$0 = $this$runBlocking;
               this.label = 1;
               if (TestCorKt.test1(this) == var5) {
                  return var5;
               }
               break;
            case 1:
               $this$runBlocking = (CoroutineScope)this.L$0;
               ResultKt.throwOnFailure($result);
               break;
            default:
               throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
            }

            return Unit.INSTANCE;
         }

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

         public final Object invoke(Object var1, Object var2) {
            return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
         }
      }), 1, (Object)null);
   }

可以看到代码经过编译器编译后依然是调用Builders.runBlocking()函数,先不关注该函数的函数体,看一下该函数的参数定义:

public fun <T> runBlocking(
context: CoroutineContext = EmptyCoroutineContext, 
block: suspend CoroutineScope.() -> T
): T {}

可以看到编译代码中new了一个Function2函数对象做为lambda表达式block参数的值,invoke()是Function2的入口函数。invoke()函数首先调用create()函数创建一个Continuation对象,然后再调用该对象的invokeSuspend()方法。下面看一下test1()方法是怎么样构造一个Continuation对象的:

   @Nullable
   public static final Object test1(@NotNull Continuation $completion) {
      Object $continuation;
      label20: {
         if ($completion instanceof <undefinedtype>) {
            $continuation = (<undefinedtype>)$completion;
            if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
               ((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
               break label20;
            }
         }

         $continuation = new ContinuationImpl($completion) {
            // $FF: synthetic field
            Object result;
            int label;

            @Nullable
            public final Object invokeSuspend(@NotNull Object $result) {
               this.result = $result;
               this.label |= Integer.MIN_VALUE;
               return TestCorKt.test1(this);
            }
         };
      }

      Object $result = ((<undefinedtype>)$continuation).result;
      Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
      String var1;
      boolean var2;
      switch(((<undefinedtype>)$continuation).label) {
      case 0:
         ResultKt.throwOnFailure($result);
         var1 = "test1";
         var2 = false;
         System.out.println(var1);
         ((<undefinedtype>)$continuation).label = 1;
         if (DelayKt.delay(100L, (Continuation)$continuation) == var5) {
            return var5;
         }
         break;
      case 1:
         ResultKt.throwOnFailure($result);
         break;
      default:
         throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
      }

      var1 = "test1-end";
      var2 = false;
      System.out.println(var1);
      return Unit.INSTANCE;
   }

可以看到编译后的代码中每次调用test1都会传入一个Continuation对象$completion,第一次调用test1()时ContinuationImpl.label=0,因此label & Integer.MIN_VALUE != 0是false,因此会执行new ContinuationImpl()逻辑。当执行了invokeSuspend()函数时会令 this.label |= Integer.MIN_VALUE,因此label & Integer.MIN_VALUE != 0为true,会break label20。

从源码看挂起

一个简单的代码断 runBlock {launch {}}, launch()函数可以新建一个谐程并start,这一过程的源码:

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
}

launch()函数返回一个Job对象,Coroutine类是Job类的子类,LazyStandaloneCoroutine类继承于StandaloneCoroutine类,StandaloneCoroutine类又继承于AbstractCoroutine类,最终会调用AbstractCoroutine类的start()方法:

public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
        initParentJob()
        start(block, receiver, this)
    }

start参数是一个函数类型对象CoroutineStart,因此这里调用start()方法即是调用CoroutineStart类中对应参数的invoke()方法:

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

一般情况下这里的类型为DEFAULT,几条路径都会调用createCoroutineUnintercepted()函数:

internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(receiver: R, completion: Continuation<T>) =
    runSafely(completion) {
        createCoroutineUnintercepted(receiver, completion).intercepted().resumeCancellableWith(Result.success(Unit))
    }

createCoroutineUnintercepted()函数对应的源码是没有函数体的,估计也是由编译器生成相应代码来创建一个Continuation对象,intercepted()方法与线程调度有关,最后会调用resumeCancellableWith()方法:

public fun <T> Continuation<T>.resumeCancellableWith(result: Result<T>) = when (this) {
    is DispatchedContinuation -> resumeCancellableWith(result)
    else -> resumeWith(result)
}

这里的this is DispatchedContinuation为true:

//DispatchedContinuation.kt
inline fun resumeCancellableWith(result: Result<T>) {
        val state = result.toState()
        if (dispatcher.isDispatchNeeded(context)) {
            _state = state
            resumeMode = MODE_CANCELLABLE
            dispatcher.dispatch(context, this)
        } else {
            executeUnconfined(state, MODE_CANCELLABLE) {
                if (!resumeCancelled()) {
                    resumeUndispatchedWith(result)
                }
            }
        }
    }
//class CoroutineDispatcher.kt
public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true

这里dispatcher被申明为CoroutineDispatcher,这里它的实际类型为BlockingEventLoop,因此会调用dispatcher.dispatch(context, this),这里的this是一个DispatchedContinueContinuation对象,其实现了Runnable接口,其run()方法在其父类DispatchedTask中进行重写,主要是恢复续体:

	//DispatchedTask.kt
    public final override fun run() {
        val taskContext = this.taskContext
        var fatalException: Throwable? = null
        try {
            val delegate = delegate as DispatchedContinuation<T>
            val continuation = delegate.continuation
            val context = continuation.context
            val state = takeState() // NOTE: Must take state in any case, even if cancelled
            withCoroutineContext(context, delegate.countOrElement) {
                val exception = getExceptionalResult(state)
                val job = if (resumeMode.isCancellableMode) context[Job] else null
                /*
                 * Check whether continuation was originally resumed with an exception.
                 * If so, it dominates cancellation, otherwise the original exception
                 * will be silently lost.
                 */
                if (exception == null && job != null && !job.isActive) {
                    val cause = job.getCancellationException()
                    cancelResult(state, cause)
                    continuation.resumeWithStackTrace(cause)
                } else {
                    if (exception != null) continuation.resumeWithException(exception)
                    else continuation.resume(getSuccessfulResult(state))
                }
            }
        } catch (e: Throwable) {
            // This instead of runCatching to have nicer stacktrace and debug experience
            fatalException = e
        } finally {
            val result = runCatching { taskContext.afterTask() }
            handleFatalException(fatalException, result.exceptionOrNull())
        }
    }

下面看一下dispatch分发过程:

//EventLoopImplBase.kt
public final override fun dispatch(context: CoroutineContext, block: Runnable) = enqueue(block)

    public fun enqueue(task: Runnable) {
        if (enqueueImpl(task)) {
            // todo: we should unpark only when this delayed task became first in the queue
            unpark()
        } else {
            DefaultExecutor.enqueue(task)
        }
    }

    private fun enqueueImpl(task: Runnable): Boolean {
        _queue.loop { queue ->
            if (isCompleted) return false // fail fast if already completed, may still add, but queues will close
            when (queue) {
                null -> if (_queue.compareAndSet(null, task)) return true
                is Queue<*> -> {
                    when ((queue as Queue<Runnable>).addLast(task)) {
                        Queue.ADD_SUCCESS -> return true
                        Queue.ADD_CLOSED -> return false
                        Queue.ADD_FROZEN -> _queue.compareAndSet(queue, queue.next())
                    }
                }
                else -> when {
                    queue === CLOSED_EMPTY -> return false
                    else -> {
                        // update to full-blown queue to add one more
                        val newQueue = Queue<Runnable>(Queue.INITIAL_CAPACITY, singleConsumer = true)
                        newQueue.addLast(queue as Runnable)
                        newQueue.addLast(task)
                        if (_queue.compareAndSet(queue, newQueue)) return true
                    }
                }
            }
        }
    }

可以看到这要是调用enqueueImpl()方法将一个DispatchedContinuation对象添加到EventLoopImplBase中的_queue队列尾部。
在这里插入图片描述
当runblock()方法中执行eventLoop?.processNextEvent()方法时,会从eventLoop中拿出该task执行,运行其run()方法,最终会执行continuation.resume(getSuccessfulResult(state))恢复续体。
还记得上一节编译协程后的代码中会多一个Continuation对象吗,Continuation是一个虚类,参考[ Kotlin协程源码分析-3 调用挂起函数.]可知其实现类如上图所示,此处调用Continuation对象的resumeWith()方法,即会调用实现类BaseContinuationImpl对象的方法:

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) {
            // Invoke "resume" debug probe on every resumed continuation, so that a debugging library infrastructure
            // can precisely track what part of suspended callstack was already resumed
            probeCoroutineResumed(current)
            with(current) {
                val completion = completion!! // fail fast when trying to resume continuation without completion
                val outcome: Result<Any?> =
                    try {
                        //会调用子类的方法,执行协程的业务相关的运算逻辑
                        val outcome = invokeSuspend(param)
                        //与上一节的IntrinsicsKt.getCOROUTINE_SUSPENDED()对应
                        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) {
                	//BaseContinuationImpl对象在循环中的作用即是执行协程的运算逻辑
                    // unrolling recursion via loop
                    current = completion
                    param = outcome
                } else {
                	//断点发现此处completion是一个DeferredCoroutine实例,会调用其父类AbstractCoroutine的方法,outcome即为计算结果
                    // top-level completion reached -- invoke and return
                    completion.resumeWith(outcome)
                    return
                }
            }
        }
    }

这段代码主要完成两项功能:

  1. 调用协程体中的计算逻辑,判断是否挂起
  2. 挂起的计算完成,则恢复挂起的协程

调用协程体中的计算逻辑是通过调用invokeSuspend()方法实现的,恢复协程则是通过AbstractCoroutine的resumeWith()方法实现。这里先不分析协议恢复过程,先通过一个小例子来自己创建一个协程:

class MyCoroutine() : Continuation<String> {
    override fun resumeWith(result: Result<String>) {
        println("MyCoroutine 回调resumeWith 返回的结果 " + result.getOrNull())
    }

    override val context: CoroutineContext
        get() = kotlin.coroutines.EmptyCoroutineContext
}

//定义一个suspend方法,用于模拟计算过程
suspend fun mySuspendFun(): String {
    return "hello1";
}

@InternalCoroutinesApi
fun testOne() {
    val myCoroutineFun: suspend () -> String = {
    	//1.协程执行计算的地方
        println("返回 hello结果")
        mySuspendFun()
    }
    val myCoroutine = MyCoroutine()
    //对应源码中 block.startCoroutineCancellable(receiver, completion)
    val coroutine = myCoroutineFun.startCoroutineCancellable(myCoroutine)
}

@InternalCoroutinesApi
fun main() {
    testOne()
}

运行结果为:

返回 hello结果
MyCoroutine 回调resumeWith 返回的结果 hello1

根据源码此处编写的lambda表达式myCoroutineFun即是对应launch()函数中block的代码,即协程中的计算逻辑。因此当调用startCoroutineCancellable()方法后,最终会走到Continuation对象的resumeWith()方法,调用其中的invokeSuspend()方法会触发myCoroutineFun逻辑的执行,接着会调用到completion.resumeWith(outcome)这句,这里的completion即是通过参数传入的myCoroutine对象,即会调用MyCoroutine中的resumeWith()方法。

协程的恢复

先看一个简单的例子,这个例子中用了delay()函数和await()函数:

fun main(args: Array<String>) =
    runBlocking<Unit> { // 新建并启动 blocking 协程,运行在 main 线程上,等待所有子协程运行完成后才会结束
        launch(Dispatchers.Unconfined) { // 新建并启动 launch 协程,没有指定所运行线程,一开始运行在调用者所在的 main 线程上
            println("${Thread.currentThread().name} : launch start")
            yield()
            async(Dispatchers.Default) { // 新建并启动 async 协程,运行在 Dispatchers.Default 的线程池中
                println("${Thread.currentThread().name} : async start")
                delay(1000)  // 挂起 async 协程 100 ms
                println("${Thread.currentThread().name} : async end")
            }.await() // 挂起 launch 协程,直到 async 协程结束
            println("${Thread.currentThread().name} : launch end")
        }
    }

运行结果为:

main : launch start
DefaultDispatcher-worker-1 : async start

1.通过delay()看协程恢复与异步调用

delay()函数的定义:

//kotlinx.coroutines.Delay.kt
public suspend fun delay(timeMillis: Long) {
    if (timeMillis <= 0) return // don't delay
    return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
        cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
    }
}

可以看到调用suspendCancellableCoroutine()函数,挂起了当前协程:

public suspend inline fun <T> suspendCancellableCoroutine(
    crossinline block: (CancellableContinuation<T>) -> Unit
): T =
    suspendCoroutineUninterceptedOrReturn { uCont ->
        val cancellable = CancellableContinuationImpl(uCont.intercepted(), resumeMode = MODE_CANCELLABLE)
        // NOTE: Before version 1.1.0 the following invocation was inlined here, so invocation of this
        // method indicates that the code was compiled by kotlinx.coroutines < 1.1.0
        // cancellable.initCancellability()
        block(cancellable)
        cancellable.getResult()
    }

可以看到构造了一个CancellableContinuationImpl对象,最后通过其getResult()方法获取协程恢复时的结果,回想一下,这一过程是不是和上一节小例子中MyCoroutine有点类似,MyCoroutine类中重写的方法resumeWith(result:Resul<>)的作用就是将协程恢复时的结果回调出去。block lambda表达式执行的是DefaultExecutor类的父类EventLoopImplBase中的方法:

 public override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
        val timeNanos = delayToNanos(timeMillis)
        if (timeNanos < MAX_DELAY_NS) {
            val now = nanoTime()
            DelayedResumeTask(now + timeNanos, continuation).also { task ->
                continuation.disposeOnCancellation(task)
                schedule(now, task)
            }
        }
    }

这里的timeMillis是需要延迟的时间,然后构造了一个DelayedResumeTask对象,disposeOnCancellation是与协程的取消有关,schedule()的定义为:

public fun schedule(now: Long, delayedTask: DelayedTask) {
        when (scheduleImpl(now, delayedTask)) {
            SCHEDULE_OK -> if (shouldUnpark(delayedTask)) unpark()
            SCHEDULE_COMPLETED -> reschedule(now, delayedTask)
            SCHEDULE_DISPOSED -> {} // do nothing -- task was already disposed
            else -> error("unexpected result")
        }
    }

首先是调用scheduleImpl()方法进一步处理这个task:

private fun scheduleImpl(now: Long, delayedTask: DelayedTask): Int {
        if (isCompleted) return SCHEDULE_COMPLETED
        val delayedQueue = _delayed.value ?: run {
            _delayed.compareAndSet(null, DelayedTaskQueue(now))
            _delayed.value!!
        }
        return delayedTask.scheduleTask(now, delayedQueue, this)
    }

先判断_delayed.value 是否为null,若为null则新构造一个DelayedTaskQueue对象,并赋值给delayedQueue,然后调用delayedTask的scheduleTask()方法,传入的参数是当前的时间、延迟队列和事件循环:

        fun scheduleTask(now: Long, delayed: DelayedTaskQueue, eventLoop: EventLoopImplBase): Int {
            if (_heap === DISPOSED_TASK) return SCHEDULE_DISPOSED // don't add -- was already disposed
            delayed.addLastIf(this) { firstTask ->
                if (eventLoop.isCompleted) return SCHEDULE_COMPLETED // non-local return from scheduleTask
                if (firstTask == null) {
                    delayed.timeNow = now
                } else {
                    val firstTime = firstTask.nanoTime
                    // compute min(now, firstTime) using a wrap-safe check
                    val minTime = if (firstTime - now >= 0) now else firstTime
                    // update timeNow only when going forward in time
                    if (minTime - delayed.timeNow > 0) delayed.timeNow = minTime
                }
                if (nanoTime - delayed.timeNow < 0) nanoTime = delayed.timeNow
                true
            }
            return SCHEDULE_OK
        }

这里的this是当前的DelayedResumeTask对象,并将其添加的延迟队列delayedQueue尾部,并返回SCHEDULE_OK。回到前面的when语句,可知SCHEDULE_OK对应的执行分支为判断shouldUnpark(delayedTask),若为true则调用unpark()唤醒当前线程。这里的线程即是DefaultExecutor类中的thread,其定义为:

    override val thread: Thread
        get() = _thread ?: createThreadSync()

private fun createThreadSync(): Thread {
        return _thread ?: Thread(this, THREAD_NAME).apply {
            _thread = this
            isDaemon = true
            start()
        }
    }

可以看到只要thread第一次被读,就会新建并运行一个名字为THREAD_NAME = "kotlinx.coroutines.DefaultExecutor"的线程,其target对象是DefaultExecutor,其实现了Runnable接口,其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
                    if (shutdownNanos == Long.MAX_VALUE) {
                        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
                        parkNanos = parkNanos.coerceAtMost(KEEP_ALIVE_NANOS) // limit wait time anyway
                }
                if (parkNanos > 0) {
                    // check if shutdown was requested and bail out in this case
                    if (isShutdownRequested) return
                    parkNanos(this, parkNanos)
                }
            }
        } finally {
            _thread = null // this thread is dead
            acknowledgeShutdownIfNeeded()
            unregisterTimeLoopThread()
            // recheck if queues are empty after _thread reference was set to null (!!!)
            if (!isEmpty) thread // recreate thread if it is needed
        }
    }

run()方法中主要运行了一个while循环,先调用processNextEvent()去处理下一个到期的task对象:

override fun processNextEvent(): Long {
        // unconfined events take priority
        if (processUnconfinedEvent()) return nextTime
        // queue all delayed tasks that are due to be executed
        val delayed = _delayed.value
        if (delayed != null && !delayed.isEmpty) {
            val now = nanoTime()
            while (true) {
                delayed.removeFirstIf {
                    if (it.timeToExecute(now)) {
                        enqueueImpl(it)
                    } else
                        false
                } ?: break // quit loop when nothing more to remove or enqueueImpl returns false on "isComplete"
            }
        }
        // then process one event from queue
        dequeue()?.run()
        return nextTime
    }

该断代码也主要执行一个while循环,判断队列首原始是否耗完delay事件,若到达执行时间,则将其从delayed队列中移除,并加入到_queue队列中,再跳出循环,然后调用dequeue()从_queue队首移除一个task来执行,下面则会执行DelayedResumeTask的run()方法,最终会调用CancellableContinuationImp

private inner class DelayedResumeTask(
        nanoTime: Long,
        private val cont: CancellableContinuation<Unit>
    ) : DelayedTask(nanoTime) {
        override fun run() { with(cont) { resumeUndispatched(Unit) } }
        override fun toString(): String = super.toString() + cont.toString()
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值