快捷搜索:  汽车  科技

协程工作原理讲解(一文彻底搞懂协程的挂起与恢复)

协程工作原理讲解(一文彻底搞懂协程的挂起与恢复)还记得delay()方法我们我们传递进去的是哪个continution吗?对,就是deal1()方法里面创建的匿名类继承自ContinuationImpl 所以这块current就是自己的匿名类continution,也就是在1除。所以会调用deal1()方法自己的suspendInvoke()方法,因为在开始的时候也就是2处,我们将自conpletion进行了一次赋值,把要操作的continution换成了自己传递进来的continution也就是父协程的continution,所以deal1()恢复后,下一次循环调用的invokeSuspend就是父协程的invokeSuspend()方法,对于我们的demo也就是调用的goodTest()的invokeSuspend()方法,这样就是完成状态的流转。最后调用这个方法的时候也就是goodTest()方法调用resumeWith()方法时

这一篇需要对协程有一定理解,从源码角度深入理解协程的挂起与恢复。

直接上代码

GlobalScope.launch { val data = goodeTest() print(data) } Thread.sleep(3000) } suspend fun goodTest(): String { val num = deal1() val user = deal2(num) val strong = deal3(user) return strong } suspend fun deal1(): Int { delay(22) return 2 } suspend fun deal2(num: Int): User { val user = User(11 "1") delay(2000) return user } suspend fun deal3(user: User): String { delay(300) return "ss" }

上字节码。学习原理还是要看字节码。网上重组过的字节码理解起来总感觉差点意思。这次我直接上未修改的,带着一步一步理解协程是怎么挂起恢复的。可以直接跳过这个总览,最后再回来看这个总览。后面的我都会一点点解释。

package com.zs.myapplication.coroutine; import com.zs.myapplication.bean.User; import kotlin.Metadata; import kotlin.ResultKt; import kotlin.Unit; import kotlin.coroutines.Continuation; import kotlin.coroutines.CoroutineContext; import kotlin.coroutines.intrinsics.IntrinsicsKt; import kotlin.coroutines.jvm.internal.Boxing; import kotlin.coroutines.jvm.internal.ContinuationImpl; import kotlin.jvm.Functions.Function2; import kotlin.jvm.internal.Intrinsics; import kotlinx.coroutines.BuildersKt; import kotlinx.coroutines.CoroutineScope; import kotlinx.coroutines.CoroutineStart; import kotlinx.coroutines.DelayKt; import kotlinx.coroutines.GlobalScope; import org.jetbrains.annotations.Notnull; import org.jetbrains.annotations.Nullable; @Metadata( mv = {1 6 0} k = 2 d1 = {"\u0000 \n\u0000\n\u0002\u0010\b\n\u0002\b\u0002\n\u0002\u0018\u0002\n\u0002\b\u0003\n\u0002\u0010\u000e\n\u0002\b\u0004\n\u0002\u0010\u0002\n\u0000\u001a\u0011\u0010\u0000\u001a\u00020\u0001H\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\u0002\u001a\u0019\u0010\u0003\u001a\u00020\u00042\u0006\u0010\u0005\u001a\u00020\u0001H\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\u0006\u001a\u0019\u0010\u0007\u001a\u00020\b2\u0006\u0010\t\u001a\u00020\u0004H\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\n\u001a\u0011\u0010\u000b\u001a\u00020\bH\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\u0002\u001a\u0006\u0010\f\u001a\u00020\r\u0082\u0002\u0004\n\u0002\b\u0019¨\u0006\u000e"} d2 = {"deal1" "" "(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;" "deal2" "Lcom/zs/myapplication/bean/User;" "num" "(ILkotlin/coroutines/Continuation;)Ljava/lang/Object;" "deal3" "" "user" "(Lcom/zs/myapplication/bean/User;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;" "goodTest" "main" "" "My_Application.app.main"} ) public final class GoodAnalisyKt { public static final void main() { BuildersKt.launch$default((CoroutineScope)GlobalScope.INSTANCE (CoroutineContext)null (CoroutineStart)null (Function2)(new Function2((Continuation)null) { int label; @Nullable public final Object invokeSuspend(@NotNull Object $result) { Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED(); Object var10000; switch(this.label) { case 0: ResultKt.throwOnFailure($result); this.label = 1; var10000 = GoodAnalisyKt.goodTest(this); if (var10000 == var3) { return var3; } break; case 1: ResultKt.throwOnFailure($result); var10000 = $result; break; default: throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); } String data = (String)var10000; System.out.print(data); return Unit.INSTANCE; } @NotNull public final Continuation create(@Nullable Object value @NotNull Continuation completion) { Intrinsics.checkNotNullParameter(completion "completion"); Function2 var3 = new <anonymous constructor>(completion); return var3; } public final Object invoke(Object var1 Object var2) { return ((<undefinedtype>)this.create(var1 (Continuation)var2)).invokeSuspend(Unit.INSTANCE); } }) 3 (Object)null); Thread.sleep(3000L); } // $FF: synthetic method public static void main(String[] var0) { main(); } @Nullable public static final Object goodTest(@NotNull Continuation var0) { Object $continuation; label37: { if (var0 instanceof <undefinedtype>) { $continuation = (<undefinedtype>)var0; if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) { ((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE; break label37; } } $continuation = new ContinuationImpl(var0) { // $FF: synthetic field Object result; int label; @Nullable public final Object invokeSuspend(@NotNull Object $result) { this.result = $result; this.label |= Integer.MIN_VALUE; return GoodAnalisyKt.goodTest(this); } }; } Object var10000; label31: { Object var6; label30: { Object $result = ((<undefinedtype>)$continuation).result; var6 = IntrinsicsKt.getCOROUTINE_SUSPENDED(); switch(((<undefinedtype>)$continuation).label) { case 0: ResultKt.throwOnFailure($result); ((<undefinedtype>)$continuation).label = 1; var10000 = deal1((Continuation)$continuation); if (var10000 == var6) { return var6; } break; case 1: ResultKt.throwOnFailure($result); var10000 = $result; break; case 2: ResultKt.throwOnFailure($result); var10000 = $result; break label30; case 3: ResultKt.throwOnFailure($result); var10000 = $result; break label31; default: throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); } int num = ((Number)var10000).intValue(); ((<undefinedtype>)$continuation).label = 2; var10000 = deal2(num (Continuation)$continuation); if (var10000 == var6) { return var6; } } User user = (User)var10000; ((<undefinedtype>)$continuation).label = 3; var10000 = deal3(user (Continuation)$continuation); if (var10000 == var6) { return var6; } } String strong = (String)var10000; return strong; } @Nullable public static final Object deal1(@NotNull Continuation var0) { Object $continuation; label20: { if (var0 instanceof <undefinedtype>) { $continuation = (<undefinedtype>)var0; if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) { ((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE; break label20; } } $continuation = new ContinuationImpl(var0) { // $FF: synthetic field Object result; int label; @Nullable public final Object invokeSuspend(@NotNull Object $result) { this.result = $result; this.label |= Integer.MIN_VALUE; return GoodAnalisyKt.deal1(this); } }; } Object $result = ((<undefinedtype>)$continuation).result; Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED(); switch(((<undefinedtype>)$continuation).label) { case 0: ResultKt.throwOnFailure($result); ((<undefinedtype>)$continuation).label = 1; if (DelayKt.delay(22L (Continuation)$continuation) == var3) { return var3; } break; case 1: ResultKt.throwOnFailure($result); break; default: throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); } return Boxing.boxInt(2); } @Nullable public static final Object deal2(int var0 @NotNull Continuation var1) { Object $continuation; label20: { if (var1 instanceof <undefinedtype>) { $continuation = (<undefinedtype>)var1; if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) { ((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE; break label20; } } $continuation = new ContinuationImpl(var1) { // $FF: synthetic field Object result; int label; Object L$0; @Nullable public final Object invokeSuspend(@NotNull Object $result) { this.result = $result; this.label |= Integer.MIN_VALUE; return GoodAnalisyKt.deal2(0 this); } }; } Object $result = ((<undefinedtype>)$continuation).result; Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED(); User user; switch(((<undefinedtype>)$continuation).label) { case 0: ResultKt.throwOnFailure($result); user = new User(11 "1"); ((<undefinedtype>)$continuation).L$0 = user; ((<undefinedtype>)$continuation).label = 1; if (DelayKt.delay(2000L (Continuation)$continuation) == var5) { return var5; } break; case 1: user = (User)((<undefinedtype>)$continuation).L$0; ResultKt.throwOnFailure($result); break; default: throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); } return user; } @Nullable public static final Object deal3(@NotNull User var0 @NotNull Continuation var1) { Object $continuation; label20: { if (var1 instanceof <undefinedtype>) { $continuation = (<undefinedtype>)var1; if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) { ((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE; break label20; } } $continuation = new ContinuationImpl(var1) { // $FF: synthetic field Object result; int label; @Nullable public final Object invokeSuspend(@NotNull Object $result) { this.result = $result; this.label |= Integer.MIN_VALUE; return GoodAnalisyKt.deal3((User)null this); } }; } Object $result = ((<undefinedtype>)$continuation).result; Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED(); switch(((<undefinedtype>)$continuation).label) { case 0: ResultKt.throwOnFailure($result); ((<undefinedtype>)$continuation).label = 1; if (DelayKt.delay(300L (Continuation)$continuation) == var4) { return var4; } break; case 1: ResultKt.throwOnFailure($result); break; default: throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); } return "ss"; } }

1.我们先看goodTest()的字节码

@Nullable public static final Object goodTest(@NotNull Continuation var0) { Object $continuation; label37: { if (var0 instanceof <undefinedtype>) { $continuation = (<undefinedtype>)var0; if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) { ((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE; break label37; } } $continuation = new ContinuationImpl(var0) { 生成一个continuation类型的匿名类 // $FF: synthetic field Object result; int label; @Nullable public final Object invokeSuspend(@NotNull Object $result) { this.result = $result; this.label |= Integer.MIN_VALUE; return GoodAnalisyKt.goodTest(this); //自己调自己 } }; } Object var10000; label31: { Object var6; label30: { Object $result = ((<undefinedtype>)$continuation).result; var6 = IntrinsicsKt.getCOROUTINE_SUSPENDED(); switch(((<undefinedtype>)$continuation).label) { case 0: ResultKt.throwOnFailure($result); ((<undefinedtype>)$continuation).label = 1; var10000 = deal1((Continuation)$continuation); if (var10000 == var6) { return var6; } break; case 1: ResultKt.throwOnFailure($result); var10000 = $result; break; case 2: ResultKt.throwOnFailure($result); var10000 = $result; break label30; case 3: ResultKt.throwOnFailure($result); var10000 = $result; break label31; default: throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); } int num = ((Number)var10000).intValue(); ((<undefinedtype>)$continuation).label = 2; var10000 = deal2(num (Continuation)$continuation); if (var10000 == var6) { return var6; } } User user = (User)var10000; ((<undefinedtype>)$continuation).label = 3; var10000 = deal3(user (Continuation)$continuation); if (var10000 == var6) { return var6; } } String strong = (String)var10000; return strong; }

我们知道协程的启动是通过GlobalScop.launch{}方法,这里面会创建一个suspendLamada,并且也会向下传递一个continuation。所以goodTest的入参continuation就是从这个suspendLamada传入的,并且会在启动的时候调用方法invokeSuspend()方法。也就是总览java文件里面的invokeSuspend()方法 还是看一下吧,如下

public static final void main() { BuildersKt.launch$default((CoroutineScope)GlobalScope.INSTANCE (CoroutineContext)null (CoroutineStart)null (Function2)(new Function2((Continuation)null) { int label; @Nullable public final Object invokeSuspend(@NotNull Object $result) { Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED(); Object var10000; switch(this.label) { case 0: ResultKt.throwOnFailure($result); this.label = 1; var10000 = GoodAnalisyKt.goodTest(this); if (var10000 == var3) { return var3; } break; case 1: ResultKt.throwOnFailure($result); var10000 = $result; break; default: throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); } String data = (String)var10000; System.out.print(data); return Unit.INSTANCE; } @NotNull public final Continuation create(@Nullable Object value @NotNull Continuation completion) { Intrinsics.checkNotNullParameter(completion "completion"); Function2 var3 = new <anonymous constructor>(completion); return var3; } public final Object invoke(Object var1 Object var2) { return ((<undefinedtype>)this.create(var1 (Continuation)var2)).invokeSuspend(Unit.INSTANCE); } }) 3 (Object)null); Thread.sleep(3000L); }

这块会调用invokeSuspend()方法,然后根据label状态机case 0调用goodTest()方法并且传递continution。这个contiuntion又是包裹了completion的,completion是调用GlobalScope.launch{}方法所创建的StandaloneCoroutine如下

协程工作原理讲解(一文彻底搞懂协程的挂起与恢复)(1)

协程工作原理讲解(一文彻底搞懂协程的挂起与恢复)(2)

这个StandaloneCoroutine是一个AbstractCoroutine 后面恢复的实话会用到这块。 接着goodTest()往下讲,在进入switch语句的case 0后,调用deal1()方法,然后在传入一个continuation这个continuation是包裹了父协程的contiunation多读写这几句理解下。 然后看下deal1()方法,也会创建一个匿名类集成自ContinuationImpl的continution并接受goodTest()传递下来的continution,然后同样进入case0 调用delay()函数,传入自己创建的ContinuationImpl 然后返回COROUTINE_SUSPENDED一段时候会调用这个continuation的resumeWith()方法 这个时候就是恢复了,重点来了 看下resumeWith()方法的实现类

协程工作原理讲解(一文彻底搞懂协程的挂起与恢复)(3)

所以挂起就是在这块挂起的。当需要恢复的时候也是调用的这块。

还记得delay()方法我们我们传递进去的是哪个continution吗?对,就是deal1()方法里面创建的匿名类继承自ContinuationImpl 所以这块current就是自己的匿名类continution,也就是在1除。所以会调用deal1()方法自己的suspendInvoke()方法,因为在开始的时候也就是2处,我们将自conpletion进行了一次赋值,把要操作的continution换成了自己传递进来的continution也就是父协程的continution,所以deal1()恢复后,下一次循环调用的invokeSuspend就是父协程的invokeSuspend()方法,对于我们的demo也就是调用的goodTest()的invokeSuspend()方法,这样就是完成状态的流转。最后调用这个方法的时候也就是goodTest()方法调用resumeWith()方法时,传入的是AbstractCoroutine所以会进入标注6 完成整个协程的恢复。

至此,协程挂起恢复原理总结完成。 总结下:

  1. 协程启动(调用launch)的时候会有两个重要的Continution 一个是AbstractCoroutine 一个是suspendLamada(suspendLamada继承自BaseContinuationImpl)
  2. 当调用suspend函数时,会创建一个匿名类ContinuationImpl(继承自BaseContinuationImpl)并将父协程的contiuntion传递进来,挂起恢复都是在BaseContinuationImpl里面的resumeWith()的while(true)里面进行控制。
  3. 当函数挂起时(即函数返回COROUTINE_SUSPENDED),在BaseContinuationImpl里面return。
  4. 当函数恢复时,也是在BaseContinuationImpl里面调用了resumeWith,此时会替换continution为父协程的continution。出发父协程的invokeSuspend完成事件流转。
  5. 最终调用AbstractCoroutine类型的continution的resumeWith()方法,完成整个协程的恢复

作者:Lazurs
链接:https://juejin.cn/post/7147534071978491934
来源:稀土掘金

猜您喜欢: