一文彻底搞懂协程的挂起与恢复

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

直接上代码

    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如下

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

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

还记得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类型的continutionresumeWith()方法,完成整个协程的恢复

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

猜你喜欢

转载自blog.csdn.net/m0_64420071/article/details/127055506