kotlin的suspend对比csharp的async&await
协程的出现大大降低了异步编程的复杂度,可以让我们像写同步代码一样去写异步代码,如果没有它,那么很多异步的代码都是需要靠回调函数来一层层嵌套,这个在我之前的一篇有介绍 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函数
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");
}
我们反编译查看下编译器生成了怎样的状态机

看反编译的代码比较吃力,我还原成了正常代码,
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);

整体的执行流程如下

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是可以的

suspend fun test2() = coroutineScope {
println("test2-start")
async {
delay(100000);
}
delay(1000)
println("test2-end")
// 或者手动取消当前coroutineScope
this.cancel()
}
kotlin的suspend对比csharp的async&await的更多相关文章
- async/await 与 generator、co 的对比
之前写过一个分批预加载资源的插件,其实质便是串行执行异步,使用的方法是generator + promise -- 前几天写了一个爬虫,抓取页面的n个页面的音频资源,其也是串行执行异步,但是在使用的a ...
- Callback, Promise和Async/Await的对比
Callback, Promise和Async/Await的对比 Callback Hell getData1(function (data1) { console.log('我得到data1了') ...
- js中回调函数,promise 以及 async/await 的对比用法 对比!!!
在编程项目中,我们常需要用到回调的做法来实现部分功能,那么在js中我们有哪些方法来实现回调的? 方法1:回调函数 首先要定义这个函数,然后才能利用回调函数来调用! login: function (f ...
- 温故知新,CSharp遇见异步编程(Async/Await),聊聊异步编程最佳做法
什么是异步编程(Async/Await) Async/Await本质上是通过编译器实现的语法糖,它让我们能够轻松的写出简洁.易懂.易维护的异步代码. Async/Await是C# 5引入的关键字,用以 ...
- [.NET] 怎样使用 async & await 一步步将同步代码转换为异步编程
怎样使用 async & await 一步步将同步代码转换为异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6079707.html ...
- [.NET] 利用 async & await 进行异步 IO 操作
利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html 序 上次,博主 ...
- 关于异步执行(Async/await)的理解(转发)
原文地址: http://blog.jobbole.com/85787/ 同步编程与异步编程 通常情况下,我们写的C#代码就是同步的,运行在同一个线程中,从程序的第一行代码到最后一句代码顺序执行.而异 ...
- Async/Await FAQ
From time to time, I receive questions from developers which highlight either a need for more inform ...
- 【TypeScript】如何在TypeScript中使用async/await,让你的代码更像C#。
[TypeScript]如何在TypeScript中使用async/await,让你的代码更像C#. async/await 提到这个东西,大家应该都很熟悉.最出名的可能就是C#中的,但也有其它语言也 ...
- async/await 异步编程(转载)
转载地址:http://www.cnblogs.com/teroy/p/4015461.html 前言 最近在学习Web Api框架的时候接触到了async/await,这个特性是.NET 4.5引入 ...
随机推荐
- KingbaseES 普通表在线改为分区表案例
对大表进行分区,但避免长时间锁表 假设您有一个应用程序,该应用程序具有一个巨大的表,并且需要始终可用.它变得如此之大,以至于在不对其进行分区的情况下对其进行管理变得越来越困难.但是,您又不能使表脱机以 ...
- K8S_删除Pod总结
K8S 不能直接删除Pod,直接删除Pod,会被Deployment重启 删除前,必须先删除对应的Deployment 例子: // 查出Pod [root@k8s-master ~]# kubect ...
- 《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(18)-Fiddler如何接口测试,妈妈再也不担心我不会接口测试了
1.简介 Fiddler最大的优势在于抓包,我们大部分使用的功能也在抓包的功能上,fiddler做接口测试也是非常方便的. 领导或者开发给你安排接口测试的工作任务,但是没有给你接口文档(由于开发周期没 ...
- Linux云主机安全入侵排查步骤
导语 经常有用户报障系统被植入恶意程序,如挖矿软件.ddos攻击病毒.syn映射攻击病毒等,可以按照以下流程为用户排查入侵病毒类型: 一.定位病毒进程 对于用户反馈云主机性能卡顿,CPU和内存占用较高 ...
- 微软出品自动化神器Playwright,不用写一行代码(Playwright+Java)系列(一) 之 环境搭建及脚本录制
一.前言 半年前,偶然在视频号刷到某机构正在直播讲解Playwright框架的使用,就看了一会,感觉还不错,便被种草,就想着自己有时间也可以自己学一下,这一想着就半年多过去了. 读到这,你可能就去百度 ...
- Awvs+nessus docker版本
awvs-nessus 拉取镜像 docker pull leishianquan/awvs-nessus:v2 启动 docker run --name awvs-nessus -it -d -p ...
- 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 ...
- 【Spring】Spring bean中id和name的差异
id和name都是spring 容器中中bean 的唯一标识符. id: 一个bean的唯一标识 , 命名格式必须符合XML ID属性的命名规范 name: 可以用特殊字符,并且一个bean可以用多个 ...
- 第六章:Django 综合篇 - 17:CSRF与AJAX
CSRF(Cross-site request forgery)跨站请求伪造,是一种常见的网络攻击手段,具体内容和含义请大家自行百度. Django为我们提供了防范CSRF攻击的机制. 一.基本使用 ...
- EasyExcel实现文件导出
官网:https://www.yuque.com/easyexcel/doc/easyexcel 导出 准备工作 引入依赖 <!--EasyExcel相关依赖--> <depende ...