协程工作原理讲解(一文彻底搞懂协程的挂起与恢复)
协程工作原理讲解(一文彻底搞懂协程的挂起与恢复)还记得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如下
这个StandaloneCoroutine是一个AbstractCoroutine 后面恢复的实话会用到这块。 接着goodTest()往下讲,在进入switch语句的case 0后,调用deal1()方法,然后在传入一个continuation这个continuation是包裹了父协程的contiunation多读写这几句理解下。 然后看下deal1()方法,也会创建一个匿名类集成自ContinuationImpl的continution并接受goodTest()传递下来的continution,然后同样进入case0 调用delay()函数,传入自己创建的ContinuationImpl 然后返回COROUTINE_SUSPENDED一段时候会调用这个continuation的resumeWith()方法 这个时候就是恢复了,重点来了 看下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 完成整个协程的恢复。
至此,协程挂起恢复原理总结完成。 总结下:
- 协程启动(调用launch)的时候会有两个重要的Continution 一个是AbstractCoroutine 一个是suspendLamada(suspendLamada继承自BaseContinuationImpl)
- 当调用suspend函数时,会创建一个匿名类ContinuationImpl(继承自BaseContinuationImpl)并将父协程的contiuntion传递进来,挂起恢复都是在BaseContinuationImpl里面的resumeWith()的while(true)里面进行控制。
- 当函数挂起时(即函数返回COROUTINE_SUSPENDED),在BaseContinuationImpl里面return。
- 当函数恢复时,也是在BaseContinuationImpl里面调用了resumeWith,此时会替换continution为父协程的continution。出发父协程的invokeSuspend完成事件流转。
- 最终调用AbstractCoroutine类型的continution的resumeWith()方法,完成整个协程的恢复
作者:Lazurs
链接:https://juejin.cn/post/7147534071978491934
来源:稀土掘金