首页 > 其他分享 >kotlin的suspend对比csharp的async&await

kotlin的suspend对比csharp的async&await

时间:2022-11-20 23:46:49浏览次数:57  
标签:test1 suspend csharp kotlin await 状态机 ._

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


public static Object test1(Continuation continuation) {
    CoroutineTest1 continuationTest1;
    label20:
    {
        if (continuation instanceof CoroutineTest1) {
            continuationTest1 = (CoroutineTest1) continuation;
            int i = continuationTest1.label & Integer.MIN_VALUE;
            if (i != 0) {
                continuationTest1.label -= Integer.MIN_VALUE;
            }
            break label20;
        }
        continuationTest1 = new CoroutineTest1(continuation);
    }

    Object result = (continuationTest1).result;
    Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
    String var1;
    switch ((continuationTest1).label) {
        case 0:
            ResultKt.throwOnFailure(result);
            var1 = "test1-start";
            System.out.println(var1);
            (continuationTest1).label = 1;
            if (test2(continuationTest1) == var4) {
                return var4;
            }
            break;
        case 1:
            ResultKt.throwOnFailure(result);
            break;
        default:
            throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
    }

    var1 = "test1-end";
    System.out.println(var1);
    return Unit.INSTANCE;
}

final static class CoroutineTest1 extends ContinuationImpl {
    Object result;
    int label;

    public CoroutineTest1(@Nullable Continuation<Object> completion) {
        super(completion);
    }

    @Nullable
    public Object invokeSuspend(@NotNull Object $result) {
        this.result = $result;
        this.label |= Integer.MIN_VALUE;
        return test1(this);
    }
}

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

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

整个运行流程如下: image

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

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

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

csharp的async&await

demo

static async Task Main(string[] args)
{
   await test1();      
   Console.WriteLine("Let's Go!");
}

async Task test1(){
  Console.WriteLine("test1-start");
  await test2();
  Console.WriteLine("test1-end");

 }

async Task test2()
{
  Console.WriteLine("test2-start");
  await Task.Delay(1000);
  Console.WriteLine("test2-end");
 }

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

image
image

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

static Task CreateMainAsyncStateMachine()
{
 MainAsyncStateMachine stateMachine = new MainAsyncStateMachine
 {
  _builder = AsyncTaskMethodBuilder.Create(),
  _state = -1
 };
 stateMachine._builder.Start(ref stateMachine);
 return stateMachine._builder.Task;
}

struct MainAsyncStateMachine : IAsyncStateMachine
{
 public int _state;
 public AsyncTaskMethodBuilder _builder;
 public TaskAwaiter _waiter;
 public void MoveNext()
 {
  int num1 = this._state;
  try
  {
   TaskAwaiter awaiter;
   int num2;
   if (num1 != 0)
   {
    awaiter = UserQuery.CreateTest1AsyncStateMachine().GetAwaiter();
    if (!awaiter.IsCompleted)
    {
     Console.WriteLine("MainAsyncStateMachine######Test1AsyncStateMachine IsCompleted:false, 注册自己到Test1Async运行结束时运行");
     this._state = num2 = 0;
     this._waiter = awaiter;
     this._builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
     return;
    }
   }
   else
   {
    Console.WriteLine("MainAsyncStateMachine######Test1AsyncStateMachine IsCompleted:true");
    awaiter = this._waiter;
    this._waiter = new TaskAwaiter();
    this._state = num2 = -1;
   }
   awaiter.GetResult();
   Console.WriteLine("MainAsyncStateMachine######Let's Go!");
  }
  catch (Exception e)
  {
   this._state = -2;
   this._builder.SetException(e);
   return;
  }
  this._state = -2;
  this._builder.SetResult();
 }
 public void SetStateMachine(IAsyncStateMachine stateMachine)
 {
  this._builder.SetStateMachine(stateMachine);
 }
}

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

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

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

// 当状态机启动时会触发 状态机的MoveNext方法的调用
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中没有(如果理解有误欢迎指正)

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

async void testAsyncA(){
    testAsyncB();
    
    // 我想取消,或者下面运行出异常了 我也无法取消testAsyncB这个任务
    
}

async void testAsyncB(){
    // do long task
}

在kotlin是可以的

image
image

suspend fun test2() = coroutineScope {
    println("test2-start")
    async {
        delay(100000);
    }
    delay(1000)
    println("test2-end")
    // 或者手动取消当前coroutineScope
    this.cancel()
}

 

 

 

标签:test1,suspend,csharp,kotlin,await,状态机,._
From: https://www.cnblogs.com/yudongdong/p/16910049.html

相关文章

  • 再探Kotlin 跨平台——迁移Paging分页库至KMM
    前言KMM的发展除了靠官方社区的支持外,一些大企业的开源落地也尤为重要。从这些开源中我们需要借鉴他的设计思想和实现方式。从而在落地遇到问题时,寻得更多的解决办法。上周,S......
  • iterator/generator 和 async/await 的实现
    1.iterator/generator简介1.1iterator1.1.1iterator的作用通过迭代器可以返回一个集合中的每一个项.我们常用的for...of循环和...扩展运算符都是基于iterato......
  • Kotlin 起步
    https://kotlinlang.org/docs/getting-started.html起步Kotlin是一个现代化且成熟的编程语言,旨在使开发者更加快乐。它简洁,安全,可以与Java和其他编程语言相交互,同时......
  • kotlin的流畅性
    一、关于运算符的重载kotlin的运算符重载和c++的运算符重载比较类似,使用operator的方式:operatorfunxxx的方式比如重载类Complex的乘号dataclassComplex(valreal:......
  • 又一巨头从 Java 迁移到 Kotlin,简直很无语。。
    出品|OSC开源社区(ID:oschina2013)Meta发布了一篇博客表示,正在将其Android应用的Java代码迁移到Kotlin,并分享了这一过程中的一些经验。该公司认为,Kotlin是一种流......
  • Mp3文件标签信息读取和写入(Kotlin)
    原文:Mp3文件标签信息读取和写入(Kotlin)-Stars-One的杂货小窝最近准备抽空完善了自己的星之小说下载器(JavaFx应用),发现下载下来的mp3文件没有对应的标签也是了解可以......
  • kotlin 内部迭代和延迟计算
    一、内部的迭代函数filter(e->返回true和false)判断数值是否加入新的数组map(e->返回调整e之后的值)对数组中每个数进行调整,并存入新的数组reduce(total,e->返回......
  • 【JS】874- 为何在 JavaScript 中使用顶层 await?
    原文地址:WhyShouldYouUseTop-levelAwaitinJavaScript?[1]原文作者:MahdhiRezvi[2]译者:Chor作为一门非常灵活和强大的语言,JavaScript对现代web产生了深远的影响。......
  • kotlin lambda
    一、lambda表达式的语法{parameterlist->body}kotlin和java的lambda表达式的语法存在一定程度的差异:1、kotlin在通常的情况下不允许有return语句,而java则可以有零个......
  • Android开发Compose版本、Kotlin 版本、KSP版本版本对应关系
    Android开发Compose版本、Kotlin版本、KSP版本版本对应关系是要遵循官方给出的,不然容易出锅甚至编译都不过,即使编译通过也可能导致潜在崩溃ComposeCompiler版本和兼......