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

    对大表进行分区,但避免长时间锁表 假设您有一个应用程序,该应用程序具有一个巨大的表,并且需要始终可用.它变得如此之大,以至于在不对其进行分区的情况下对其进行管理变得越来越困难.但是,您又不能使表脱机以 ...

  2. K8S_删除Pod总结

    K8S 不能直接删除Pod,直接删除Pod,会被Deployment重启 删除前,必须先删除对应的Deployment 例子: // 查出Pod [root@k8s-master ~]# kubect ...

  3. 《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(18)-Fiddler如何接口测试,妈妈再也不担心我不会接口测试了

    1.简介 Fiddler最大的优势在于抓包,我们大部分使用的功能也在抓包的功能上,fiddler做接口测试也是非常方便的. 领导或者开发给你安排接口测试的工作任务,但是没有给你接口文档(由于开发周期没 ...

  4. Linux云主机安全入侵排查步骤

    导语 经常有用户报障系统被植入恶意程序,如挖矿软件.ddos攻击病毒.syn映射攻击病毒等,可以按照以下流程为用户排查入侵病毒类型: 一.定位病毒进程 对于用户反馈云主机性能卡顿,CPU和内存占用较高 ...

  5. 微软出品自动化神器Playwright,不用写一行代码(Playwright+Java)系列(一) 之 环境搭建及脚本录制

    一.前言 半年前,偶然在视频号刷到某机构正在直播讲解Playwright框架的使用,就看了一会,感觉还不错,便被种草,就想着自己有时间也可以自己学一下,这一想着就半年多过去了. 读到这,你可能就去百度 ...

  6. Awvs+nessus docker版本

    awvs-nessus 拉取镜像 docker pull leishianquan/awvs-nessus:v2 启动 docker run --name awvs-nessus -it -d -p ...

  7. The 19th Zhejiang Provincial Collegiate Programming Contest

    目录 A.JB Loves Math B.JB Loves Comma C. JB Wants to Earn Big Money G. Easy Glide I. Barbecue L. Candy ...

  8. 【Spring】Spring bean中id和name的差异

    id和name都是spring 容器中中bean 的唯一标识符. id: 一个bean的唯一标识 , 命名格式必须符合XML ID属性的命名规范 name: 可以用特殊字符,并且一个bean可以用多个 ...

  9. 第六章:Django 综合篇 - 17:CSRF与AJAX

    CSRF(Cross-site request forgery)跨站请求伪造,是一种常见的网络攻击手段,具体内容和含义请大家自行百度. Django为我们提供了防范CSRF攻击的机制. 一.基本使用 ...

  10. EasyExcel实现文件导出

    官网:https://www.yuque.com/easyexcel/doc/easyexcel 导出 准备工作 引入依赖 <!--EasyExcel相关依赖--> <depende ...