协程的出现大大降低了异步编程的复杂度,可以让我们像写同步代码一样去写异步代码,如果没有它,那么很多异步的代码都是需要靠回调函数来一层层嵌套,这个在我之前的一篇有介绍 rxjava回调地狱-kotlin协程来帮忙
本篇文章主要介绍
-
kotlin的suspend函数在编译生成了怎样的代码 -
csharp的async&await在编译生成了怎么样的代码 -
这两者相比较,引发怎样的思考
kotlin的suspend函数demo
这里针对kotlin的语法以及协程的具体用法细节不过多介绍,就当你已了解
稍微注意下runBlocking函数比较特别,
如下图:它接受了一个suspend的block函数
所以我上面的demo这里面有其实有三个suspend函数!
在idea我们可以把这个kotlin代码反编译成java代码
这个反编译后的java代码 有很多报错是无法直接copy出来运行的(这就没有csharp做的好,csharp反编译出来的代码至少不会报红),
看代码的确是一个状态机控制函数和一个匿名类,还原成正常的java代码如下:
比如test1函数
publicstaticObjecttest1(Continuationcontinuation){
CoroutineTest1continuationTest1;
label20:
{
if(continuationinstanceofCoroutineTest1){
continuationTest1=(CoroutineTest1)continuation;
inti=continuationTest1.label&Integer.MIN_VALUE;
if(i!=0){
continuationTest1.label-=Integer.MIN_VALUE;
}
breaklabel20;
}
continuationTest1=newCoroutineTest1(continuation);
}
Objectresult=(continuationTest1).result;
Objectvar4=IntrinsicsKt.getCOROUTINE_SUSPENDED();
Stringvar1;
switch((continuationTest1).label){
case0:
ResultKt.throwOnFailure(result);
var1="test1-start";
System.out.println(var1);
(continuationTest1).label=1;
if(test2(continuationTest1)==var4){
returnvar4;
}
break;
case1:
ResultKt.throwOnFailure(result);
break;
default:
thrownewIllegalStateException("callto'resume'before'invoke'withcoroutine");
}
var1="test1-end";
System.out.println(var1);
returnUnit.INSTANCE;
}
finalstaticclassCoroutineTest1extendsContinuationImpl{
Objectresult;
intlabel;
publicCoroutineTest1(@NullableContinuation<Object>completion){
super(completion);
}
@Nullable
publicObjectinvokeSuspend(@NotNullObject$result){
this.result=$result;
this.label|=Integer.MIN_VALUE;
returntest1(this);
}
}
其他的函数也类似,完整的代码请查看:
https://gist.github.com/yuzd/cf67048777f0eb8fc1b3757f5bf9e8f3
整个运行流程如下:
kotlin协程的挂起点是怎么控制的,异步操作执行完后它知道从哪里恢复?
不难看出来suspend函数其实在编译后是变成了状态机,将我们顺序执行的代码,转换成了回调的形式 父suspend函数里面调用子suspend函数,其实是把自己传给了子suspend状态机,如果子函数挂起了,等子函数恢复后直接调用父函数(因为通过状态机的label来控制走不同逻辑,去恢复当时的调用堆栈)
这就是协程的挂起与恢复机制了
csharp的async&await
demo
staticasyncTaskMain(string[]args)
{
awaittest1();
Console.WriteLine("Let'sGo!");
}
asyncTasktest1(){
Console.WriteLine("test1-start");
awaittest2();
Console.WriteLine("test1-end");
}
asyncTasktest2()
{
Console.WriteLine("test2-start");
awaitTask.Delay(1000);
Console.WriteLine("test2-end");
}
我们反编译查看下编译器生成了怎样的状态机
看反编译的代码比较吃力,我还原成了正常代码,
staticTaskCreateMainAsyncStateMachine()
{
MainAsyncStateMachinestateMachine=newMainAsyncStateMachine
{
_builder=AsyncTaskMethodBuilder.Create(),
_state=-1
};
stateMachine._builder.Start(refstateMachine);
returnstateMachine._builder.Task;
}
structMainAsyncStateMachine:IAsyncStateMachine
{
publicint_state;
publicAsyncTaskMethodBuilder_builder;
publicTaskAwaiter_waiter;
publicvoidMoveNext()
{
intnum1=this._state;
try
{
TaskAwaiterawaiter;
intnum2;
if(num1!=0)
{
awaiter=UserQuery.CreateTest1AsyncStateMachine().GetAwaiter();
if(!awaiter.IsCompleted)
{
Console.WriteLine("MainAsyncStateMachine######Test1AsyncStateMachineIsCompleted:false,注册自己到Test1Async运行结束时运行");
this._state=num2=0;
this._waiter=awaiter;
this._builder.AwaitUnsafeOnCompleted(refawaiter,refthis);
return;
}
}
else
{
Console.WriteLine("MainAsyncStateMachine######Test1AsyncStateMachineIsCompleted:true");
awaiter=this._waiter;
this._waiter=newTaskAwaiter();
this._state=num2=-1;
}
awaiter.GetResult();
Console.WriteLine("MainAsyncStateMachine######Let'sGo!");
}
catch(Exceptione)
{
this._state=-2;
this._builder.SetException(e);
return;
}
this._state=-2;
this._builder.SetResult();
}
publicvoidSetStateMachine(IAsyncStateMachinestateMachine)
{
this._builder.SetStateMachine(stateMachine);
}
}
完整代码请查看 https://github.com/yuzd/asyncawait_study
可以看出来,和kotlin其实原理差不多,都是生成一个函数加一个状态机
区别是csharp的函数就是创建一个状态机且启动它
//当状态机启动时会触发状态机的MoveNext方法的调用
stateMachine._builder.Start(refstateMachine);
整体的执行流程如下
ps:最右边的是展示如果有多个await 那么就会对应这个状态机的多个状态
这两者相比较,引发怎样的思考
通过查看kotlin和csharp的实现方式,我发现kotlin的生成的状态机(ContinuationImpl的实现)都是有继承关系的, 比如demo中的test2继承了test1,test继承了main(通过构造函数传递的)
然而csharp中没有这样的关系
这也带来了两者最大的区别,kotlin的协程绑定了scope的概念,一旦scope被取消,那么scope绑定的所有的协程也都被取消。
这点好像在csharp中没有(如果理解有误欢迎指正)
这在实际应用中是怎么个区别呢,举个例子
asyncvoidtestAsyncA(){
testAsyncB();
//我想取消,或者下面运行出异常了我也无法取消testAsyncB这个任务
}
asyncvoidtestAsyncB(){
//dolongtask
}
在kotlin是可以的
suspendfuntest2()=coroutineScope{
println("test2-start")
async{
delay(100000);
}
delay(1000)
println("test2-end")
//或者手动取消当前coroutineScope
this.cancel()
}
1、IT大王遵守相关法律法规,由于本站资源全部来源于网络程序/投稿,故资源量太大无法一一准确核实资源侵权的真实性;
2、出于传递信息之目的,故IT大王可能会误刊发损害或影响您的合法权益,请您积极与我们联系处理(所有内容不代表本站观点与立场);
3、因时间、精力有限,我们无法一一核实每一条消息的真实性,但我们会在发布之前尽最大努力来核实这些信息;
4、无论出于何种目的要求本站删除内容,您均需要提供根据国家版权局发布的示范格式
《要求删除或断开链接侵权网络内容的通知》:https://itdw.cn/ziliao/sfgs.pdf,
国家知识产权局《要求删除或断开链接侵权网络内容的通知》填写说明: http://www.ncac.gov.cn/chinacopyright/contents/12227/342400.shtml
未按照国家知识产权局格式通知一律不予处理;请按照此通知格式填写发至本站的邮箱 wl6@163.com