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

本篇文章主要介绍

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

kotlin的suspend函数demo

image

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

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

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

image

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

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

image

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

image

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

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

整个运行流程如下:

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

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

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

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

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

kotlin的suspend对比csharp的async&await的更多相关文章

  1. async/await 与 generator、co 的对比

    之前写过一个分批预加载资源的插件,其实质便是串行执行异步,使用的方法是generator + promise -- 前几天写了一个爬虫,抓取页面的n个页面的音频资源,其也是串行执行异步,但是在使用的a ...

  2. Callback, Promise和Async/Await的对比

    Callback, Promise和Async/Await的对比 Callback Hell getData1(function (data1) { console.log('我得到data1了') ...

  3. js中回调函数,promise 以及 async/await 的对比用法 对比!!!

    在编程项目中,我们常需要用到回调的做法来实现部分功能,那么在js中我们有哪些方法来实现回调的? 方法1:回调函数 首先要定义这个函数,然后才能利用回调函数来调用! login: function (f ...

  4. 温故知新,CSharp遇见异步编程(Async/Await),聊聊异步编程最佳做法

    什么是异步编程(Async/Await) Async/Await本质上是通过编译器实现的语法糖,它让我们能够轻松的写出简洁.易懂.易维护的异步代码. Async/Await是C# 5引入的关键字,用以 ...

  5. [.NET] 怎样使用 async & await 一步步将同步代码转换为异步编程

    怎样使用 async & await 一步步将同步代码转换为异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6079707.html  ...

  6. [.NET] 利用 async & await 进行异步 IO 操作

    利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html  序 上次,博主 ...

  7. 关于异步执行(Async/await)的理解(转发)

    原文地址: http://blog.jobbole.com/85787/ 同步编程与异步编程 通常情况下,我们写的C#代码就是同步的,运行在同一个线程中,从程序的第一行代码到最后一句代码顺序执行.而异 ...

  8. Async/Await FAQ

    From time to time, I receive questions from developers which highlight either a need for more inform ...

  9. 【TypeScript】如何在TypeScript中使用async/await,让你的代码更像C#。

    [TypeScript]如何在TypeScript中使用async/await,让你的代码更像C#. async/await 提到这个东西,大家应该都很熟悉.最出名的可能就是C#中的,但也有其它语言也 ...

  10. async/await 异步编程(转载)

    转载地址:http://www.cnblogs.com/teroy/p/4015461.html 前言 最近在学习Web Api框架的时候接触到了async/await,这个特性是.NET 4.5引入 ...

随机推荐

  1. dp-背包模型

    一:01背包问题模型 1 题目: 有一个箱子容量为 V,同时有 n 个物品,每个物品有一个体积(正整数). 要求 n 个物品中,任取若干个装入箱内,使箱子的剩余空间为最小. 输入格式 第一行是一个整数 ...

  2. Elasticsearch与MySQL对应关系表

    MySQL 中的数据库(DataBase),等价于 ES 中的索引(Index). MySQL 中一个数据库下面有 N 张表(Table),等价于1个索引 Index 下面有 N 多类型(Type). ...

  3. Kubernetes(k8s)通过环境变量将 Pod 信息呈现给容器

    Downward API 有两种方式可以将 Pod 和 Container 字段呈现给运行中的容器: 环境变量 卷文件 这两种呈现 Pod 和 Container 字段的方式统称为 Downward ...

  4. MySQL 中 datetime 和 timestamp 的区别与选择

    MySQL 中常用的两种时间储存类型分别是datetime和 timestamp.如何在它们之间选择是建表时必要的考虑.下面就谈谈他们的区别和怎么选择. 1 区别 1.1 占用空间 类型 占据字节 表 ...

  5. PAT (Basic Level) Practice 1009 说反话 分数 20

    给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出. 输入格式: 测试输入包含一个测试用例,在一行内给出总长度不超过 80 的字符串.字符串由若干单词和若干空格组成,其中单词是由英文字母(大小 ...

  6. C语言下for循环的一点技巧总结

    for循环是普遍应用与各种计算机语言的一种循环方式. 一般情况下, for循环规则:for(条件一:条件二:条件三) 条件一为满足条件,也就是条件一为1时,进入这个for循环.条件二为循环条件,也就是 ...

  7. 洛谷P7960 [NOIP2021] 报数 (筛法)

    禁止报的数的生成规则与埃式筛法类似,考虑用筛法预处理可以报出的数字列表和不可报出的数字,从而 O(1) 回答每一组询问. 用check函数判断数字中是否含有7,用nx[i]记录数字i的下一个合法数. ...

  8. 它让你1小时精通RabbitMQ消息队列(新增死信处理)

    支持.NET/.NET Framework/.NET Core RabbitMQ作为一款主流的消息队列工具早已广受欢迎.相比于其它的MQ工具,RabbitMQ支持的语言更多.功能更完善. 本文提供一种 ...

  9. Redis高可用之主从复制原理演进分析

    Redis高可用之主从复制原理演进分析 在很久之前写过一篇 Redis 主从复制原理的简略分析,基本是一个笔记类文章. 一.什么是主从复制 1.1 什么是主从复制 主从复制,从名字可以看出,至少需要 ...

  10. 华为路由器RIP路由协议配置命令

    RIP路由协议配置 rip 创建开启协议进程 network + ip 对指定网段接口使能RIP功能IP地址是与路由器直连的网段 debugging rip 1 查看RIP定期更新情况 termina ...