在 Emit 代码中如何await一个异步方法
0. 前言
首先立马解释一波为啥会有这样一篇伪标题的Demo随笔呢?
不是本人有知识误区,或者要误人子弟
因为大家都知道emit写出来的都是同步方法,不可能await,至少现在这么多年来没有提供对应的功能
这是之前某天在微信群看见讨论怎么emit一个异步方法并包装异步结构,简单几句文字也未能清晰的表达
所以趁着元旦节放假有点时间,
简单列举三种我知道方式去达到这样的效果
三种方法都是绕过emit直接书写emit代码,而是将对应逻辑转到其他方法中,最后emit调用方法达到效果
Demo 说明
原始方法是个延迟2秒之后返回55的方法:
public static async Task<int> GetV()
{
await Task.Delay(2000);
return 55;
}
现在我们需要把 55 的结果加 6 ,让最终的结果变为 61
我们的测试方法是这样,会输出一些简单的时间,帮助我们了解执行顺序和异步情况
private static async Task Test(MethodInfo method, MethodInfo awaitMehtod)
{
var caller = CreateCaller(method, awaitMehtod);
Console.WriteLine($"Start {awaitMehtod.Name} at: {DateTime.Now}.");
var task = caller();
Console.WriteLine($"Call done at: {DateTime.Now}.");
var number = await task;
Console.WriteLine($"Hello {number} at: {DateTime.Now}.");
Console.WriteLine($"End at: {DateTime.Now}.");
Console.WriteLine();
}
1. ContinueWith
public static Func<Task<int>> CreateCaller(MethodInfo method, MethodInfo awaitMehtod)
{
var m = new DynamicMethod(Guid.NewGuid().ToString("N"), typeof(Task<int>), Type.EmptyTypes);
var il = m.GetILGenerator();
il.Emit(OpCodes.Call, method);
il.Emit(OpCodes.Call, typeof(Program).GetMethod(nameof(Program.AddSixUseContinueWith))); // 这里是差异点
il.Emit(OpCodes.Ret);
return m.CreateDelegate(typeof(Func<Task<int>>)) as Func<Task<int>>;
}
public static Task<int> AddSixUseContinueWith(Task<int> task)
{
return task.ContinueWith(i =>
{
Console.WriteLine($"AddSixUseContinueWith is: {DateTime.Now}.");
return i.Result + 6;
});
}
测试结果:
Start AddSixUseContinueWith at: 2021/1/2 13:34:55.
Call done at: 2021/1/2 13:34:55.
AddSixUseContinueWith is: 2021/1/2 13:34:57.
Hello 61 at: 2021/1/2 13:34:57.
End at: 2021/1/2 13:34:57.
优点
还是真正的异步
缺点
成本比较大,毕竟这样没有了状态机等等优化,(成本在 ns 级别哦,不是大家想的 ms哦)
2. GetAwaiter().GetResult()
public static Func<Task<int>> CreateCaller(MethodInfo method, MethodInfo awaitMehtod)
{
var m = new DynamicMethod(Guid.NewGuid().ToString("N"), typeof(Task<int>), Type.EmptyTypes);
var il = m.GetILGenerator();
il.Emit(OpCodes.Call, method);
il.Emit(OpCodes.Call, typeof(Program).GetMethod(nameof(Program.AddSixUseAwaiter))); // 这里是差异点
il.Emit(OpCodes.Ret);
return m.CreateDelegate(typeof(Func<Task<int>>)) as Func<Task<int>>;
}
public static Task<int> AddSixUseAwaiter(Task<int> task)
{
var r = task.ConfigureAwait(false).GetAwaiter().GetResult() + 6;
Console.WriteLine($"AddSixUseAwaiter is: {DateTime.Now}.");
return Task.FromResult(r);
}
测试结果:
Start AddSixUseAwaiter at: 2021/1/2 13:34:57.
AddSixUseAwaiter is: 2021/1/2 13:34:59.
Call done at: 2021/1/2 13:34:59.
Hello 61 at: 2021/1/2 13:34:59.
End at: 2021/1/2 13:34:59.
优点
执行时间上消耗很小
缺点
当然这样 异步都变成了同步,所以可能会在某些情况下我们操作不当的代码从而导致失去异步方法的优势
3. async/await
public static Func<Task<int>> CreateCaller(MethodInfo method, MethodInfo awaitMehtod)
{
var m = new DynamicMethod(Guid.NewGuid().ToString("N"), typeof(Task<int>), Type.EmptyTypes);
var il = m.GetILGenerator();
il.Emit(OpCodes.Call, method);
il.Emit(OpCodes.Call, typeof(Program).GetMethod(nameof(Program.AddSixUseAsyncAwait))); // 这里是差异点
il.Emit(OpCodes.Ret);
return m.CreateDelegate(typeof(Func<Task<int>>)) as Func<Task<int>>;
}
public static async Task<int> AddSixUseAsyncAwait(Task<int> task)
{
var r = await task;
Console.WriteLine($"AddSixUseAsyncAwait is: {DateTime.Now}.");
return r + 6;
}
测试结果:
Start AddSixUseAsyncAwait at: 2021/1/2 13:34:59.
Call done at: 2021/1/2 13:34:59.
AddSixUseAsyncAwait is: 2021/1/2 13:35:01.
Hello 61 at: 2021/1/2 13:35:01.
End at: 2021/1/2 13:35:01.
优点
async / await 本身的优势都没有损失
缺点
原本想在 emit 中 对result的处理逻辑 必须迁移到 async / await 方法中,emit代码必须好好设计
完整Demo放在
https://github.com/fs7744/grocery/blob/main/csharp/emit_await/EmitAwaitDemo/Program.cs
分享不易,如果能给予一点动力,不胜感激:关注一下本人的开源项目: Norns.Urd
在 Emit 代码中如何await一个异步方法的更多相关文章
- 【转】 C#中Finally的一个不太常见的用法
原文地址:http://www.cnblogs.com/listhome/p/3664300.html 最近在看.net BCL 传送门 的源码. 在 System.Collections.Concu ...
- C#中Finally的一个不太常见的用法
最近在看.net BCL 传送门 的源码. 在 System.Collections.Concurrent.ConcurrentQueue 中看到一段有意思的代码.注意这段代码是写在Concurren ...
- C语言初学者代码中的常见错误与瑕疵(7)
问题: 矩形的个数 在一个3*2的矩形中,可以找到6个1*1的矩形,4个2*1的矩形3个1*2的矩形,2个2*2的矩形,2个3*1的矩形和1个3*2的矩形,总共18个矩形.给出A,B,计算可以从中找到 ...
- SpriteBuilder代码中弱引用(weak)需要注意的地方
比如在GameScene类中有一个弹出菜单层实例的引用,我们有: @implementation GameScene{ //other ivars __weak GameMenuLayer *_pop ...
- iOS中如何创建一个滑出式导航面板(1)
本文将介绍如何创建类似Facebook和Path iOS程序中的滑出式导航面板. 向右滑动 滑出式设计模式可以让开发者在程序中添加常用的导航功能,又不会浪费屏幕上宝贵的空间.用户可以在任意时间滑出导航 ...
- Spring Boot中只能有一个WebMvcConfigurationSupport配置类
首先将结论写文章的最前面,一个项目中只能有一个继承WebMvcConfigurationSupport的@Configuration类(使用@EnableMvc效果相同),如果存在多个这样的类,只有一 ...
- java高级用法之:在JNA中将本地方法映射到JAVA代码中
目录 简介 Library Mapping Function Mapping Invocation Mapping 防止VM崩溃 性能考虑 总结 简介 不管是JNI还是JNA,最终调用的都是nativ ...
- 实际案例:在现有代码中通过async/await实现并行
一项新技术或者一个新特性,只有你用它解决实际问题后,才能真正体会到它的魅力,真正理解它.也期待大家能够多分享解一些解决实际问题的内容. 在我们遭遇“黑色30秒”问题的过程中,切身体会到了异步的巨大作用 ...
- 在现有代码中通过async/await实现并行
在现有代码中通过async/await实现并行 一项新技术或者一个新特性,只有你用它解决实际问题后,才能真正体会到它的魅力,真正理解它.也期待大家能够多分享解一些解决实际问题的内容. 在我们遭遇“黑色 ...
随机推荐
- moviepy音视频剪辑:TextClip.list(font)和search搜索字体报错UnicodeDecodeError:utf-8 codec cannott decode byte 问题
☞ ░ 前往老猿Python博文目录 ░ 在moviepy2.0.0.Dev版本中,执行如下语句: from moviepy.editor import * TextClip.search('gb', ...
- Node.js 应用---定时给自己发送邮件
参照传智播客的视频所写代码. js代码: //引用superagent包,用于服务器发送http请求 const request = require('superagent'); //导入cheeri ...
- linux 解压缩文件(tar和zip)
tar -zcvf /home/xahot.tar.gz /xahottar -zcvf 打包后生成的文件名全路径 要打包的目录 sudo apt install zip unzip zip -r a ...
- chrome打开Axure Rp导出的html文件提示需要安装Axure Rp插件解决办法
1.确保chrome安装了Axure Rp扩展插件,如果按照页面的提示去下载,但是打不开的话就是被墙了,贴上科学的上网梯子https://www.lanzous.com/i7i0wuh,直接下载打开就 ...
- 在iframe中获取另一个iframe中的元素
$(top.parent.iframeId).contents().find("#selector") //iframeId为iframe的id名称
- NOIP2020 浙江 游记
day - ? 由于 CSP-S 的失利,感觉这一次 NOIP 的心态反而是非常的淡定,感觉反正已经炸过一次了,再炸一次好像也没什么,就抱着这样的心态去考试的. day 1 考试当天起晚了,到考场的时 ...
- 题解-洛谷P4859 已经没有什么好害怕的了
洛谷P4859 已经没有什么好害怕的了 给定 \(n\) 和 \(k\),\(n\) 个糖果能量 \(a_i\) 和 \(n\) 个药片能量 \(b_i\),每个 \(a_i\) 和 \(b_i\) ...
- 最近有安装了一次hadoop集群,NameNode启动失败,及原因
最近有安装了一次hadoop集群,NameNode启动失败,查看日志,找到以下原因: 遇到的异常1: org.apache.hadoop.hdfs.server.common.Inconsistent ...
- 在虚拟机中安装Linux系统CentOS7详细教程!!!超详细!!!!一看就会!!!手把手教学!!!
一.CentOS的下载 CentOS是免费版,推荐在官网上直接下载.https://www.centos.org/download/ DVD ISO:普通光盘完整安装版镜像,可离线安装到计算机硬盘上, ...
- pandas 学习 第14篇:索引和选择数据
数据框和序列结构中都有轴标签,轴标签的信息存储在Index对象中,轴标签的最重要的作用是: 唯一标识数据,用于定位数据 用于数据对齐 获取和设置数据集的子集. 本文重点关注如何对序列(Series)和 ...