kotlin 协程是如何在底层实现的?

Sou*_*a B 5 java kotlin kotlin-coroutines

Kotlin 协程是该语言的关键功能之一。我很好奇他们是如何实现的。所以我写了这个 kotlin 代码片段,

CoroutineScope(Dispatchers.IO).launch {
    someFun(6)  // A suspend fun with single int argument.
}  // Which is converted into a synthetic method with an extra argument of continuation type added at the end.
Run Code Online (Sandbox Code Playgroud)

然后将其编译为字节码,然后将其反编译为Java(在Android Studio中)。我懂了,

BuildersKt.launch$default(CoroutineScopeKt.CoroutineScope((CoroutineContext)Dispatchers.getIO()), (CoroutineContext)null, (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
   private CoroutineScope p$;
   Object L$0;
   int label;
            
   @Nullable
   public final Object invokeSuspend(@NotNull Object $result) {
      Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
      CoroutineScope $this$launch;
      switch(this.label) {
      case 0:
         ResultKt.throwOnFailure($result);
         $this$launch = this.p$;
         MainActivity var10000 = MainActivity.this;
         this.L$0 = $this$launch;
         this.label = 1;
         if (var10000.someFun(6, this) == var3) {
            return var3;
         }
         break;
      case 1:
         $this$launch = (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.checkNotNullParameter(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);
   }
}), 3, (Object)null);
Run Code Online (Sandbox Code Playgroud)

我理解了这段代码的某些部分。但仍有一些部分没有进入我的脑海。

根据这个答案,挂起函数在每个挂起点被分成多个延续,然后通过连续调用从头开始执行invokeSuspend。每次invokeSuspend调用when时,都会执行相应的switch case,然后更新lable. 所以下次调用时invokeSuspend,它会运行下一个案例。当 <it?> 被调用来挂起该函数时,它会停止调用invokeSuspend并等待恢复调用。一旦接到恢复电话,它就会再次正常继续。这就是我所知道的一切。

在第一行,Function2 构造函数接受 Continuation 的 null 对象。但为什么Function2类有一个单参数构造函数呢?我不知道Function2的实现。我搜索了它的源代码,但没有得到它。如果可能的话,我想要一个 .net 源代码的链接kotlin.jvm.functions.Function2。

case0: 的末尾,if (var10000.someFun(6, this) == var3) return var3;实际上意味着什么。

在先前对 invokeSuspend 的调用完成后,谁负责调用 invokeSuspend。它如何知道最后一个 invokeSuspend 案例何时执行?

3, (Object)null最后一个参数代表什么launch$default

Dai*_*oto 0

让我尝试回答(我不是 Kotlin 专家,我从未在工作中使用过)。

\n
\n

我想要 kotlin.jvm.functions.Function2 源代码的链接。

\n
\n

我在 Kotlin 的 JVM stdlib 实现中找到了它。

\n

https://github.com/JetBrains/kotlin/blob/53c523052073b5e39e6cc601ed5278292cfbf4bc/libraries/stdlib/jvm/runtime/kotlin/jvm/functions/Functions.kt#L22-L25

\n
public interface Function2<in P1, in P2, out R> : Function<R> {\n    public operator fun invoke(p1: P1, p2: P2): R\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我们使用它的原因可能是因为runBlocking需要suspend CoroutineScope.() -> T作为参数。CoroutineScope.()是 的扩展方法CoroutineScope,它是一个suspend函数,在与 Java 互操作时需要带有 2 个参数的函数。因此,CoroutineScope实例作为第一个参数传入,Continuation协程的延续实例作为第二个参数传入。

\n
\n

case0: 的最后, if (var10000.someFun(6, this) == var3) return var3; 是什么意思?实际上意味着。

\n
\n

我相信这是为了检查 是否someFun仍然暂停。\n如果它返回与(表明它已暂停)相同的值var3,那么当前函数也应该暂停并返回var3。

\n

对此的一个很好的解释可以在The Beginner\xe2\x80\x99s Guide to Kotlin Coroutine Internals中找到。\n文章摘录:

\n
\n

我们可以看到挂起函数可以返回 IntrinsicsKt.getCOROUTINE_SUSPENDED() 或函数的实际结果。如果返回值是前一种情况,我们就知道我们正在调用的挂起函数还没有准备好,我们需要挂起当前函数并返回。

\n
\n

回到你的问题。

\n
\n

在先前对 invokeSuspend 的调用完成后,谁负责调用 invokeSuspend。它如何知道最后一个 invokeSuspend 案例何时执行?

\n
\n

我相信这将是 Kotlin 的协程调度程序。\n我没有具体证据,但通过嗅探 Kotlin 的源代码,我发现了这一点:

\n

https://github.com/JetBrains/kotlin/blob/53c523052073b5e39e6cc601ed5278292cfbf4bc/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractSuspendFunctionsLowering.kt#L169

\n

它似乎正在为该invokeSuspend方法构建一个函数对象,这让我认为它将在这一层中被调用,而不是从应用程序层生成的代码(即您的代码)。

\n
\n

3、(Object)null 在 launch$default 的最后一个参数中代表什么

\n
\n

抱歉,我不太确定,因为我找不到 的源代码launch$default。\n如果我发现任何东西,请让我更新答案。

\n