经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Kotlin » 查看文章
kotlin的suspend对比csharp的async&await
来源:cnblogs  作者:俞正东  时间:2022/11/21 9:02:54  对本文有异议

协程的出现大大降低了异步编程的复杂度,可以让我们像写同步代码一样去写异步代码,如果没有它,那么很多异步的代码都是需要靠回调函数来一层层嵌套,这个在我之前的一篇有介绍 rxjava回调地狱-kotlin协程来帮忙

本篇文章主要介绍

  • kotlin的suspend函数在编译生成了怎样的代码
  • csharp的async&await在编译生成了怎么样的代码
  • 这两者相比较,引发怎样的思考

kotlin的suspend函数demo

image
image

这里针对kotlin的语法以及协程的具体用法细节不过多介绍,就当你已了解

稍微注意下runBlocking函数比较特别,

如下图:它接受了一个suspend的block函数

image
image

所以我上面的demo这里面有其实有三个suspend函数!

在idea我们可以把这个kotlin代码反编译成java代码

image
image

这个反编译后的java代码 有很多报错是无法直接copy出来运行的(这就没有csharp做的好,csharp反编译出来的代码至少不会报红),

image
image

看代码的确是一个状态机控制函数和一个匿名类,还原成正常的java代码如下:

image
image

比如test1函数

  1. public static Object test1(Continuation continuation) {
  2.     CoroutineTest1 continuationTest1;
  3.     label20:
  4.     {
  5.         if (continuation instanceof CoroutineTest1) {
  6.             continuationTest1 = (CoroutineTest1) continuation;
  7.             int i = continuationTest1.label & Integer.MIN_VALUE;
  8.             if (!= 0) {
  9.                 continuationTest1.label -= Integer.MIN_VALUE;
  10.             }
  11.             break label20;
  12.         }
  13.         continuationTest1 = new CoroutineTest1(continuation);
  14.     }
  15.     Object result = (continuationTest1).result;
  16.     Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
  17.     String var1;
  18.     switch ((continuationTest1).label) {
  19.         case 0:
  20.             ResultKt.throwOnFailure(result);
  21.             var1 = "test1-start";
  22.             System.out.println(var1);
  23.             (continuationTest1).label = 1;
  24.             if (test2(continuationTest1) == var4) {
  25.                 return var4;
  26.             }
  27.             break;
  28.         case 1:
  29.             ResultKt.throwOnFailure(result);
  30.             break;
  31.         default:
  32.             throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
  33.     }
  34.     var1 = "test1-end";
  35.     System.out.println(var1);
  36.     return Unit.INSTANCE;
  37. }
  38. final static class CoroutineTest1 extends ContinuationImpl {
  39.     Object result;
  40.     int label;
  41.     public CoroutineTest1(@Nullable Continuation<Object> completion) {
  42.         super(completion);
  43.     }
  44.     @Nullable
  45.     public Object invokeSuspend(@NotNull Object $result) {
  46.         this.result = $result;
  47.         this.label |= Integer.MIN_VALUE;
  48.         return test1(this);
  49.     }
  50. }

其他的函数也类似,完整的代码请查看:

https://gist.github.com/yuzd/cf67048777f0eb8fc1b3757f5bf9e8f3

整个运行流程如下: image

kotlin协程的挂起点是怎么控制的,异步操作执行完后它知道从哪里恢复?

不难看出来suspend函数其实在编译后是变成了状态机,将我们顺序执行的代码,转换成了回调的形式 父suspend函数里面调用子suspend函数,其实是把自己传给了子suspend状态机,如果子函数挂起了,等子函数恢复后直接调用父函数(因为通过状态机的label来控制走不同逻辑,去恢复当时的调用堆栈)

这就是协程的挂起与恢复机制了

csharp的async&await

demo

  1. static async Task Main(string[] args)
  2. {
  3.    await test1();      
  4.    Console.WriteLine("Let's Go!");
  5. }
  6. async Task test1(){
  7.   Console.WriteLine("test1-start");
  8.   await test2();
  9.   Console.WriteLine("test1-end");
  10.  }
  11. async Task test2()
  12. {
  13.   Console.WriteLine("test2-start");
  14.   await Task.Delay(1000);
  15.   Console.WriteLine("test2-end");
  16.  }

我们反编译查看下编译器生成了怎样的状态机

image
image

看反编译的代码比较吃力,我还原成了正常代码,

  1. static Task CreateMainAsyncStateMachine()
  2. {
  3.  MainAsyncStateMachine stateMachine = new MainAsyncStateMachine
  4.  {
  5.   _builder = AsyncTaskMethodBuilder.Create(),
  6.   _state = -1
  7.  };
  8.  stateMachine._builder.Start(ref stateMachine);
  9.  return stateMachine._builder.Task;
  10. }
  11. struct MainAsyncStateMachine : IAsyncStateMachine
  12. {
  13.  public int _state;
  14.  public AsyncTaskMethodBuilder _builder;
  15.  public TaskAwaiter _waiter;
  16.  public void MoveNext()
  17.  {
  18.   int num1 = this._state;
  19.   try
  20.   {
  21.    TaskAwaiter awaiter;
  22.    int num2;
  23.    if (num1 != 0)
  24.    {
  25.     awaiter = UserQuery.CreateTest1AsyncStateMachine().GetAwaiter();
  26.     if (!awaiter.IsCompleted)
  27.     {
  28.      Console.WriteLine("MainAsyncStateMachine######Test1AsyncStateMachine IsCompleted:false, 注册自己到Test1Async运行结束时运行");
  29.      this._state = num2 = 0;
  30.      this._waiter = awaiter;
  31.      this._builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
  32.      return;
  33.     }
  34.    }
  35.    else
  36.    {
  37.     Console.WriteLine("MainAsyncStateMachine######Test1AsyncStateMachine IsCompleted:true");
  38.     awaiter = this._waiter;
  39.     this._waiter = new TaskAwaiter();
  40.     this._state = num2 = -1;
  41.    }
  42.    awaiter.GetResult();
  43.    Console.WriteLine("MainAsyncStateMachine######Let's Go!");
  44.   }
  45.   catch (Exception e)
  46.   {
  47.    this._state = -2;
  48.    this._builder.SetException(e);
  49.    return;
  50.   }
  51.   this._state = -2;
  52.   this._builder.SetResult();
  53.  }
  54.  public void SetStateMachine(IAsyncStateMachine stateMachine)
  55.  {
  56.   this._builder.SetStateMachine(stateMachine);
  57.  }
  58. }

完整代码请查看 https://github.com/yuzd/asyncawait_study

可以看出来,和kotlin其实原理差不多,都是生成一个函数加一个状态机

区别是csharp的函数就是创建一个状态机且启动它

  1. // 当状态机启动时会触发 状态机的MoveNext方法的调用
  2. stateMachine._builder.Start(ref stateMachine);
image
image

整体的执行流程如下

image
image

ps:最右边的是展示如果有多个await 那么就会对应这个状态机的多个状态

这两者相比较,引发怎样的思考

通过查看kotlin和csharp的实现方式,我发现kotlin的生成的状态机(ContinuationImpl的实现)都是有继承关系的, 比如demo中的test2继承了test1,test继承了main(通过构造函数传递的)

然而csharp中没有这样的关系

这也带来了两者最大的区别,kotlin的协程绑定了scope的概念,一旦scope被取消,那么scope绑定的所有的协程也都被取消。

这点好像在csharp中没有(如果理解有误欢迎指正)

这在实际应用中是怎么个区别呢,举个例子

  1. async void testAsyncA(){
  2.     testAsyncB();
  3.     
  4.     // 我想取消,或者下面运行出异常了 我也无法取消testAsyncB这个任务
  5.     
  6. }
  7. async void testAsyncB(){
  8.     // do long task
  9. }

在kotlin是可以的

image
image
  1. suspend fun test2() = coroutineScope {
  2.     println("test2-start")
  3.     async {
  4.         delay(100000);
  5.     }
  6.     delay(1000)
  7.     println("test2-end")
  8.     // 或者手动取消当前coroutineScope
  9.     this.cancel()
  10. }

 

 

 

原文链接:https://www.cnblogs.com/yudongdong/p/16910049.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号