广告
返回顶部
首页 > 资讯 > 前端开发 > node.js >Kotlin协程的工作原理是什么
  • 757
分享到

Kotlin协程的工作原理是什么

2024-04-02 19:04:59 757人浏览 独家记忆
摘要

这篇文章主要讲解了“Kotlin协程的工作原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Kotlin协程的工作原理是什么”吧!协程的状态机这一章会

这篇文章主要讲解了“Kotlin协程的工作原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Kotlin协程的工作原理是什么”吧!

协程的状态机

这一章会以下面的代码为例解析一下协程启动,挂起以及恢复的流程:

private suspend fun getId(): String {     return GlobalScope.async(Dispatchers.io) {         delay(1000)         "hearing"     }.await() }  private suspend fun getAvatar(id: String): String {     return GlobalScope.async(Dispatchers.IO) {         delay(1000)         "avatar-$id"     }.await() }  fun main() {     GlobalScope.launch {         val id = getId()         val avatar = getAvatar(id)         println("${Thread.currentThread().name} - $id - $avatar")     } }

上面 main 方法中,GlobalScope.launch 启动的协程体在执行到 getId 后,协程体会挂起,直到 getId 返回可用结果,才会  resume launch 协程,执行到 getAvatar 也是同样的过程。协程内部实现使用状态机来处理不同的挂起点,将 GlobalScope.launch  协程体字节码反编译成 Java 代码,大致如下(有所删减):

BuildersKt.launch$default((CoroutineScope)GlobalScope.INSTANCE, (CoroutineContext)null,     (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {     int label;      public final Object invokeSuspend(   Object $result) {         Object var10000;         String id;         label17: {             CoroutineScope $this$launch;             switch(this.label) {             case 0: // a                 ResultKt.throwOnFailure($result);                 $this$launch = this.p$;                 this.label = 1; // label置为1                 var10000 = getId(this);                 if (var10000 == COROUTINE_SUSPENDED) {                     return COROUTINE_SUSPENDED;                 }                 // 若此时已经有结果,则不挂起,直接break                 break;             case 1: // b                 ResultKt.throwOnFailure($result);                 var10000 = $result;                 break;             case 2: // d                 id = (String)this.L$1;                 ResultKt.throwOnFailure($result);                 var10000 = $result;                 break label17; // 退出label17             default:                 throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");             }             // c             id = (String)var10000;             this.L$1 = id; // 将id赋给L$1             this.label = 2; // label置为2             var10000 = getAvatar(id, this);             if (var10000 == COROUTINE_SUSPENDED) {                 return COROUTINE_SUSPENDED;             }         }         // e         String avatar = (String)var10000;         String var5 = var9.append(var10001.getName()).append(" - ").append(id).append(" - ").append(avatar).toString();         System.out.println(var5);         return Unit.INSTANCE;     }             public final Continuation create(   Object value,    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);     } }

这里我们根据上面的注释以及字母标签来看一下执行流程(invokeSuspend 方法会在协程体中的 suspend  函数得到结果后被调用,具体是在哪里被调用的稍后会讲到):

  • a: launch 协程体刚执行到 getId 方法时,getId 方法的返回值将是 COROUTINE_SUSPENDED, 此时直接 return,  则 launch 协程体中 getId 后面的代码暂时不会执行,即 launch 协程体被挂起(非阻塞, 该线程依旧会做其它工作)。这里将 label 置为了  1. 而若此时 getId 已经有结果(内部没有调用 delay 之类的 suspend 函数等),则不挂起,而是直接 break。

  • b: 若上面 a 中 getId 返回 COROUTINE_SUSPENDED, 则当 getId 有可用结果返回后,会重新执行 launch 协程体的  invokeSuspend 方法,根据上面的 label==1, 会执行到这里检查一下 result 没问题的话就 break, 此时 id 赋值给了  var10000。

  • c: 在 a 中若直接 break 或 在 b 中得到 getId 的结果然后 break 后,都会执行到这里,得到 id 的值并把 label  置为2。然后调用 getAvatar 方法,跟 getId 类似,若其返回 COROUTINE_SUSPENDED 则 return,协程被挂起,等到下次  invokeSuspend 被执行,否则离开 label17 接着执行后续逻辑。

  • d: 若上面 c 中 getAvatar 返回 COROUTINE_SUSPENDED, 则当 getAvatar 有可用结果返回后会重新调用  launch 协程体的 invokeSuspend 方法,此时根据 label==2 来到这里并取得之前的 id 值,检验  result(即avatar),然后break label17。

  • e: c 中直接返回了可用结果 或 d 中 break label17 后,launch 协程体中的 suspend  函数都执行完毕了,这里会执行剩下的逻辑。

suspend 函数不会阻塞线程,且 suspend 函数不一定会挂起协程,如果相关调用的结果已经可用,则继续运行而不挂起,例如 async{} 返回值  Deferred 的结果已经可用时,await()挂起函数可以直接返回结果,不用再挂起协程。

这一节看了一下 launch 协程体反编译成 Java 后的代码逻辑,关于 invokeSuspend 是何时怎么被调用的,将会在下面讲到。

协程的创建与启动

这一节以 CoroutineScope.launch {} 默认参数为例,从源码角度看看 Kotlin 协程是怎样创建与启动的:

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 }  // AbstractCoroutine.kt // receiver: StandaloneCoroutine // block: suspend StandaloneCoroutine.() -> Unit // private open class StandaloneCoroutine(...) : AbstractCoroutine<Unit>(...) {} // public abstract class AbstractCoroutine<in T>(...) : JobSupport(active), Job, Continuation<T>, CoroutineScope {} public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {     // 调用 CoroutineStart 中的 invoke 方法     start(block, receiver, this) }  public enum class CoroutineStart {     // block - StandaloneCoroutine.() -> Unit     // receiver - StandaloneCoroutine     // completion - StandaloneCoroutine<Unit>     public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>): Unit =         when (this) {             // 根据 start 参数的类型调用不同的方法             DEFAULT -> block.startCoroutineCancellable(receiver, completion)             ATOMIC -> block.startCoroutine(receiver, completion)             UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)             LAZY -> Unit // will start lazily         } }

接下来看看 startCoroutineCancellable 方法:

// receiver - StandaloneCoroutine // completion - StandaloneCoroutine<Unit> 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 类型(协程)的实例,即创建了一个协程:

public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(     receiver: R, completion: Continuation<T> ): Continuation<Unit> {     return if (this is BaseContinuationImpl) create(receiver, completion) else // ... }

调用的是 (suspend (R) -> T) 的 createCoroutineUnintercepted 方法,(suspend (R)  -> T) 就是协程体。直接看上面示例代码中 GlobalScope.launch 编译后的字节码,可以发现 CoroutineScope.launch  传入的 lambda 表达式被编译成了继承 SuspendLambda 的子类:

final class Main$main$1 extends kotlin/coroutines/JVM/internal/SuspendLambda implements kotlin/jvm/functions/Function2

其继承关系为: SuspendLambda -> ContinuationImpl -> BaseContinuationImpl ->  Continuation, 因此走 create(receiver, completion) 方法,从上面反编译出的 Java 代码可以看到 create  方法创建了一个 Continuation 实例,再看一下 Kotlin 代码编译后的字节码(包名已省略):

public final create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation; // ... NEW Main$main$1

从上面可以看到,create 方法创建了 Main$main$1 实例,而其继承自 SuspendLambda, 因此 create 方法创建的  Continuation 是一个 SuspendLambda 对象。

即 createCoroutineUnintercepted 方法创建了一个 SuspendLambda 实例。然后看看 intercepted  方法:

public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =     // 如果是ContinuationImpl类型,则调用intercepted方法,否则返回自身     // 这里的 this 是 Main$main$1 实例 - ContinuationImpl的子类     (this as? ContinuationImpl)?.intercepted() ?: this  // ContinuationImpl public fun intercepted(): Continuation<Any?> =     // context[ContinuationInterceptor]是 CoroutineDispatcher 实例     // 需要线程调度 - 返回 DispatchedContinuation,其 continuation 参数值为 SuspendLambda     // 不需要线程调度 - 返回 SuspendLambda     intercepted ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this).also { intercepted = it }  // CoroutineDispatcher // continuation - SuspendLambda -> ContinuationImpl -> BaseContinuationImpl public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =     DispatchedContinuation(this, continuation)

接下来看看 resumeCancellableWith 是怎么启动协程的,这里还涉及到Dispatchers线程调度的逻辑:

internal class DispatchedContinuation<in T>(        val dispatcher: CoroutineDispatcher,        val continuation: Continuation<T> ) : DispatchedTask<T>(MODE_ATOMIC_DEFAULT), CoroutineStackFrame, Continuation<T> by continuation {     public fun <T> Continuation<T>.resumeCancellableWith(result: Result<T>): Unit = when (this) {         // 进行线程调度,最后也会执行到continuation.resumeWith方法         is DispatchedContinuation -> resumeCancellableWith(result)         // 直接执行continuation.resumeWith方法         else -> resumeWith(result)     }      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)                 }             }         }     }      inline fun resumeUndispatchedWith(result: Result<T>) {         withCoroutineContext(context, countOrElement) {             continuation.resumeWith(result)         }     } }
  • 当需要线程调度时,则在调度后会调用 DispatchedContinuation.continuation.resumeWith 来启动协程,其中  continuation 是 SuspendLambda 实例;

  • 当不需要线程调度时,则直接调用 SuspendLambda.resumeWith 来启动协程。

resumeWith 方法调用的是父类 BaseContinuationImpl 中的 resumeWith 方法:

internal abstract class BaseContinuationImpl(public val completion: Continuation<Any?>?) : Continuation<Any?>, CoroutineStackFrame, Serializable {     public final override fun resumeWith(result: Result<Any?>) {         // ...         val outcome = invokeSuspend(param)         // ...     } }

因此,协程的启动是通过 BaseContinuationImpl.resumeWith 方法调用到了子类  SuspendLambda.invokeSuspend 方法,然后通过状态机来控制顺序运行。

协程的挂起和恢复

Kotlin 编译器会为 协程体 生成继承自 SuspendLambda 的子类,协程的真正运算逻辑都在其 invokeSuspend  方法中。上一节介绍了 launch 是怎么创建和启动协程的,在这一节我们再看看当协程代码执行到 suspend 函数后,协程是怎么被挂起的 以及 当  suspend 函数执行完成得到可用结果后是怎么恢复协程的。

Kotlin 协程的内部实现使用了 Kotlin 编译器的一些编译技术,当 suspend 函数被调用时,都有一个隐式的参数额外传入,这个参数是  Continuation 类型,封装了协程 resume 后执行的代码逻辑。

private suspend fun getId(): String {     return GlobalScope.async(Dispatchers.IO) {         delay(1000)         "hearing"     }.await() }  // Decompile成Java final Object getId(   Continuation $completion) {     // ... }

其中传入的 $completion 参数,从上一节可以看到是调用 getId 方法所在的协程体对象,也就是一个 SuspendLambda  对象。Continuation的定义如下:

public interface Continuation<in T> {     public val context: CoroutineContext      public fun resumeWith(result: Result<T>) }

将 getId 方法编译后的字节码反编译成 Java 代码如下(为便于阅读,删减及修改了部分代码):

final Object getId(   Continuation $completion) {     // 新建与启动协程     return BuildersKt.async$default((CoroutineScope)GlobalScope.INSTANCE, (CoroutineContext)Dispatchers.getIO(), (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {         int label;           public final Object invokeSuspend(   Object $result) {             switch(this.label) {             case 0:                 ResultKt.throwOnFailure($result);                 this.label = 1;                 if (DelayKt.delay(1000L, this) == COROUTINE_SUSPENDED) {                     return COROUTINE_SUSPENDED;                 }                 break;             case 1:                 ResultKt.throwOnFailure($result);                 break;             default:                 throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");             }             return "hearing";         }          // ...     }), 2, (Object)null).await($completion); // 调用 await() suspend 函数 }

结合协程的状态机一节,当上面的 launch 协程体执行到 getId 方法时, 会根据其返回值是否为 COROUTINE_SUSPENDED  来决定是否挂起,由于 getId 的逻辑是通过 async 启动一个新的协程,协程体内调用了 suspend delay 方法,然后通过 await  suspend 函数等待结果,当 async 协程没完成时, await 会返回 COROUTINE_SUSPENDED, 因此 launch 协程体的  invokeSuspend 方法直接 return COROUTINE_SUSPENDED 值执行完成,此时 launch  启动的协程处于挂起状态但不阻塞所处线程,而 async 启动的协程开始执行。

我们看一下 async 的源码:

public fun <T> CoroutineScope.async(...): Deferred<T> {     val newContext = newCoroutineContext(context)     val coroutine = if (start.isLazy) LazyDeferredCoroutine(newContext, block) else         DeferredCoroutine<T>(newContext, active = true)     coroutine.start(start, coroutine, block)     return coroutine }

默认情况下,上面的 coroutine 取 DeferredCoroutine 实例,于是我们看一下其 await 方法以及在 async  协程执行完成后,是怎么恢复 launch 协程的:

private open class DeferredCoroutine<T>(     parentContext: CoroutineContext, active: Boolean ) : AbstractCoroutine<T>(parentContext, active), Deferred<T>, SelectClause1<T> {     override suspend fun await(): T = awaitInternal() as T }  // JobSupport internal suspend fun awaitInternal(): Any? {     while (true) { // lock-free loop on state         val state = this.state         if (state !is Incomplete) {             // 已经完成,则直接返回结果             if (state is CompletedExceptionally) { // Slow path to recover stacktrace                 recoverAndThrow(state.cause)             }             return state.unboxState()         }         // 不需要重试时直接break,执行awaitSuspend         if (startInternal(state) >= 0) break     }     return awaitSuspend() // slow-path }  // suspendCoroutineUninterceptedOrReturn: 获取当前协程,且挂起当前协程(返回COROUTINE_SUSPENDED)或不挂起直接返回结果 private suspend fun awaitSuspend(): Any? = suspendCoroutineUninterceptedOrReturn { uCont ->     val cont = AwaitContinuation(uCont.intercepted(), this)     cont.disposeOnCancellation(invokeOnCompletion(ResumeAwaitOnCompletion(this, cont).asHandler))     cont.getResult() }

上面 awaitInternal 的大致逻辑是当挂起函数已经有结果时则直接返回,否则挂起父协程,然后 invokeOnCompletion 方法将  ResumeAwaitOnCompletion 插入一个队列(state.list)中,源码就不再贴出了。接着看看在 async 执行完成后是怎么调用  ResumeAwaitOnCompletion 来 resume 被挂起的协程的。注意:不要绕进 async 协程体中 delay 是怎么挂起和恢复 async  协程的这一逻辑,我们不需要关注这一层!

接着 async 协程的执行往下看,从前面可知它会调用 BaseContinuationImpl.resumeWith  方法来执行协程逻辑,我们详细看一下这个方法,在这里会执行该协程的 invokeSuspend 函数:

internal abstract class BaseContinuationImpl(     public val completion: Continuation<Any?>? ) : Continuation<Any?>, CoroutineStackFrame, Serializable {     public final override fun resumeWith(result: Result<Any?>) {         var current = this         var param = result         while (true) {             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,即协程挂起时,resumeWith 执行结束                         // 再次调用 resumeWith 时协程挂起点之后的代码才能继续执行                         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.resumeWith(outcome)                     return                 }             }         }     } }

我们从上面的源码可以看到,在 createCoroutineUnintercepted 方法中创建的 SuspendLambda 实例是  BaseContinuationImpl 的子类对象,其 completion 参数为下:

  • launch: if (isLazy) LazyStandaloneCoroutine else StandaloneCoroutine

  • async: if (isLazy) LazyDeferredCoroutine else DeferredCoroutine

上面这几个类都是 AbstractCoroutine 的子类。而根据 completion 的类型会执行不同的逻辑:

  • BaseContinuationImpl: 执行协程逻辑

  • 其它: 调用 resumeWith 方法,处理协程的状态,协程挂起后的恢复即与它有关

在上面的例子中 async 启动的协程,它也会调用其 invokeSuspend 方法执行 async 协程逻辑,假设 async  返回的结果已经可用时,即非 COROUTINE_SUSPENDED 值,此时 completion 是 DeferredCoroutine 对象,因此会调用  DeferredCoroutine.resumeWith 方法,然后返回,父协程的恢复逻辑便是在这里。

// AbstractCoroutine public final override fun resumeWith(result: Result<T>) {     val state = makeCompletinGonce(result.toState())     if (state === COMPLETING_WAITING_CHILDREN) return     afterResume(state) }

在 makeCompletingOnce 方法中,会根据 state 去处理协程状态,并执行上面插入 state.list 队列中的  ResumeAwaitOnCompletion.invoke 来恢复父协程,必要的话还会把 async  的结果给它,具体代码实现太多就不贴了,不是本节的重点。直接看 ResumeAwaitOnCompletion.invoke 方法:

private class ResumeAwaitOnCompletion<T>(     job: JobSupport, private val continuation: CancellableContinuationImpl<T> ) : Jobnode<JobSupport>(job) {     override fun invoke(cause: Throwable?) {         val state = job.state         assert { state !is Incomplete }         if (state is CompletedExceptionally) {             // Resume with with the corresponding exception to preserve it             continuation.resumeWithException(state.cause)         } else {             // resume 被挂起的协程             continuation.resume(state.unboxState() as T)         }     } }

这里的 continuation 就是 launch 协程体,也就是 SuspendLambda 对象,于是 invoke 方法会再一次调用到  BaseContinuationImpl.resumeWith 方法,接着调用 SuspendLambda.invokeSuspend, 然后根据 label  取值继续执行接下来的逻辑!

suspendCoroutineUninterceptedOrReturn

接下来我们看一下怎么将一个基于回调的方法改造成一个基于协程的 suspend 方法,要实现这个需求,重点在于  suspendCoroutineUninterceptedOrReturn 方法,根据注释,这个方法的作用是: Obtains the current  continuation instance inside suspend functions and either suspends currently  running coroutine or returns result immediately without suspension.  即获取当前协程的实例,并且挂起当前协程或不挂起直接返回结果。函数定义如下:

public suspend inline fun <T> suspendCoroutineUninterceptedOrReturn(crossinline block: (Continuation<T>) -> Any?): T {     // ... }

根据 block 的返回值,有两种情况:

  • 如果 block 返回 COROUTINE_SUSPENDED, 意味着 suspend 函数会挂起当前协程而不会立即返回结果。这种情况下, block  中的 Continuation 需要在结果可用后调用 Continuation.resumeWith 来 resume 协程。

  • 如果 block 返回的 T 是 suspend 函数的结果,则协程不会被挂起, block 中的 Continuation 不会被调用。

调用 Continuation.resumeWith 会直接在调用者的线程 resume 协程,而不会经过 CoroutineContext 中可能存在的  ContinuationInterceptor。建议使用更安全的 suspendCoroutine 方法,在其 block 中可以同步或在异步线程调用  Continuation.resume 和 Continuation.resumeWithException:

public suspend inline fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T {     contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }     return suspendCoroutineUninterceptedOrReturn { c: Continuation<T> ->         // 调用拦截器         val safe = SafeContinuation(c.intercepted())         block(safe)         safe.getOrThrow()     } }

此外除了 suspendCoroutine 方法,还有 suspendCancellableCoroutine,  suspendAtomicCancellableCoroutine, suspendAtomicCancellableCoroutineReusable  等方法都可以用来将异步回调的方法封装成 suspend 函数。

下面来看一个例子来介绍怎么将异步回调函数封装成 suspend 函数:

class NetFetcher {     // 将下面的 request 方法封装成 suspend 方法     suspend fun requestSuspend(id: Int): String = suspendCoroutine { continuation ->         request(id, object : OnResponseListener {             override fun onResponse(response: String) {                 continuation.resume(response)             }              override fun onError(error: String) {                 continuation.resumeWithException(Exception(error))             }         })     }      fun request(id: Int, listener: OnResponseListener) {         Thread.sleep(5000)         if (id % 2 == 0) {             listener.onResponse("success")         } else {             listener.onError("error")         }     }      interface OnResponseListener {         fun onResponse(response: String)         fun onError(error: String)     } }  object Main {     fun main() {         requestByCoroutine()     }      // 使用回调     private fun requestByCallback() {         NetFetcher().request(21, object : NetFetcher.OnResponseListener {             override fun onResponse(response: String) {                 println("result = $response")             }              override fun onError(error: String) {                 println("result = $error")             }         })     }      // 使用协程     private fun requestByCoroutine() {         GlobalScope.launch(Dispatchers.Main) {             val result = withContext(Dispatchers.IO) {                 try {                     NetFetcher().requestSuspend(22)                 } catch (e: Exception) {                     e.message                 }             }

为加深理解,再介绍一下 Kotlin 提供的两个借助 suspendCancellableCoroutine 实现的挂起函数: delay &  yield。

delay

delay 方法借助了 suspendCancellableCoroutine 方法来挂起协程:

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)     } }  override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {     postDelayed(Runnable {         with(continuation) { resumeUndispatched(Unit) }     }, timeMillis) }

可以看出这里 delay 的逻辑类似于 Handle 机制,将 resumeUndispatched 封装的 Runnable  放到一个队列中,在延迟的时间到达便会执行 resume 恢复协程。

yield

yield 方法作用是挂起当前协程,这样可以让该协程所在线程运行其他逻辑,当其他协程执行完成或也调用 yield  让出执行权时,之前的协程可以恢复执行。

launch(Dispatchers.Main) {     repeat(3) {         println("job1 $it")         yield()     } } launch(Dispatchers.Main) {     repeat(3) {         println("job2 $it")         yield()     } }  // output job1 0 job2 0 job1 1 job2 1 job1 2 job2 2

看一下 yield 的源码:

public suspend fun yield(): Unit = suspendCoroutineUninterceptedOrReturn sc@ { uCont ->     val context = uCont.context     // 如果协程没有调度器,或者像 Unconfined 一样没有进行调度则直接返回     val cont = uCont.intercepted() as? DispatchedContinuation<Unit> ?: return@sc Unit     if (cont.dispatcher.isDispatchNeeded(context)) {         // this is a regular dispatcher -- do simple dispatchYield         cont.dispatchYield(context, Unit)     } else {         // This is either an "immediate" dispatcher or the Unconfined dispatcher         // ...     }     COROUTINE_SUSPENDED }  // DispatchedContinuation internal fun dispatchYield(context: CoroutineContext, value: T) {     _state = value     resumeMode = MODE_CANCELLABLE     dispatcher.dispatchYield(context, this) }  public open fun dispatchYield(context: CoroutineContext, block: Runnable): Unit = dispatch(context, block)

可知 dispatchYield 会调用到 dispatcher.dispatch  方法将协程分发到调度器队列中,这样线程可以执行其他协程,等到调度器再次执行到该协程时,会 resume 该协程。

总结

通过上面协程的工作原理解析,可以从源码中发现 Kotlin 中的协程存在着三层包装:

  • 第一层包装: launch & async 返回的 Job, Deferred 继承自 AbstractCoroutine,  里面封装了协程的状态,提供了 cancel 等接口;

  • 第二层包装: 编译器生成的 SuspendLambda 子类,封装了协程的真正执行逻辑,其继承关系为 SuspendLambda ->  ContinuationImpl -> BaseContinuationImpl, 它的 completion 参数就是第一层包装实例;

  • 第三层包装: DispatchedContinuation, 封装了线程调度逻辑,它的 continuation 参数就是第二层包装实例。

感谢各位的阅读,以上就是“Kotlin协程的工作原理是什么”的内容了,经过本文的学习后,相信大家对Kotlin协程的工作原理是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: Kotlin协程的工作原理是什么

本文链接: https://www.lsjlt.com/news/66339.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Kotlin协程的工作原理是什么
    这篇文章主要讲解了“Kotlin协程的工作原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Kotlin协程的工作原理是什么”吧!协程的状态机这一章会...
    99+
    2022-10-19
  • HTTP协议的工作原理是什么
    这篇文章主要介绍“HTTP协议的工作原理是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“HTTP协议的工作原理是什么”文章能帮助大家解决问题。HTTP协议(HyperText Transfer ...
    99+
    2023-06-04
  • Kotlin协程是什么
    本文小编为大家详细介绍“Kotlin协程是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Kotlin协程是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。Kotlin协程是什么简要概括:协程是 Kotli...
    99+
    2023-07-05
  • HTTP协议的特点和工作原理是什么
    本篇内容介绍了“HTTP协议的特点和工作原理是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、HTTP简介1、简介HTTP超文本传输协...
    99+
    2023-07-02
  • kotlin协程异常处理的方法是什么
    Kotlin协程的异常处理方法有以下几种:1. 使用try/catch块:在协程内部使用try/catch块来捕获异常,并对异常进行...
    99+
    2023-09-20
    kotlin
  • 线程池的工作原理是什么
    这篇文章主要讲解了“线程池的工作原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“线程池的工作原理是什么”吧!线程池的自我介绍我是一个线程池(Thre...
    99+
    2022-10-19
  • Kotlin关于协程是什么的探究
    目录Kotlin协程是什么suspend是什么总结Kotlin协程是什么 本文只是自己经过研究后,对 Kotlin 协程的理解概括,如有偏差,还请斧正。 简要概括: 协程是 Kotl...
    99+
    2023-01-12
    Kotlin协程原理 Kotlin协程的使用 Kotlin协程并发
  • HTTP协议的工作流程是什么
    这篇“HTTP协议的工作流程是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“HTTP协议的工作流程是什么”文章吧。HTT...
    99+
    2023-06-27
  • ipsec的工作原理是什么
    IPSec(Internet Protocol Security)是一种用于保护网络通信安全的协议套件。它可以提供数据加密、数据完整...
    99+
    2023-10-10
    ipsec
  • HDFS的工作原理是什么
    这篇文章将为大家详细讲解有关HDFS的工作原理是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。HDFS的工作原理HDFS支持在计算节点之间快速传输数据。在开始阶段,它与MapReduce紧密耦合——M...
    99+
    2023-06-05
  • chatgpt的工作原理是什么
    chatgpt的工作原理是以电子邮件和电子传真方式为用户提供多媒体信息传输,利用一个基于网络的、安全的点对点通讯系统为用户进行多人的...
    99+
    2023-02-09
    chatgpt
  • NTPD的工作原理是什么
    今天小编给大家分享一下NTPD的工作原理是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Network Time Pro...
    99+
    2023-06-28
  • fastdfs的工作原理是什么
    FastDFS(Fast Distributed File System)是一个开源的分布式文件系统,主要用于解决大规模数据存储和访...
    99+
    2023-10-24
    fastdfs
  • HashMap的工作原理是什么
    原理介绍如下:HashMap是基于Hash算法实现的。(在线学习视频教程分享:java视频教程)在使用put(key,value)方法时,HashMap会根据key.hashCode()方法得出key的hash值,然后根据hash值找到对应...
    99+
    2019-10-28
    java入门 HashMap
  • docker的工作原理是什么
    Docker的工作原理主要涉及以下几个方面:1. 镜像(Image):Docker利用镜像来构建容器,镜像是一个只读的文件系统,包含...
    99+
    2023-09-27
    docker
  • RabbitMQ的工作原理是什么
    这篇文章给大家介绍RabbitMQ的工作原理是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。RabbitMQ简介在介绍RabbitMQ之前实现要介绍一下MQ,MQ是什么?MQ全称是Message Queue,可以理...
    99+
    2023-06-19
  • Git的工作原理是什么
    这篇文章主要讲解了“Git的工作原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Git的工作原理是什么”吧!本文图解Git中的最常用命令。如果你稍微理解Git的工作原理,这篇文章能够...
    99+
    2023-06-15
  • puppet的工作原理是什么
    今天小编给大家分享一下puppet的工作原理是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Puppet 的介绍什么是P...
    99+
    2023-06-28
  • Torrents的工作原理是什么
    本篇内容主要讲解“Torrents的工作原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Torrents的工作原理是什么”吧!Torrents 是如何工作的?假设 &lsquo;...
    99+
    2023-06-27
  • MapReduce的工作原理是什么
    本篇文章为大家展示了MapReduce的工作原理是什么,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。开始聊mapreduce,mapreduce是hadoop的计算框架,我学hadoop是从hive...
    99+
    2023-06-03
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作