在 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实现并行 一项新技术或者一个新特性,只有你用它解决实际问题后,才能真正体会到它的魅力,真正理解它.也期待大家能够多分享解一些解决实际问题的内容. 在我们遭遇“黑色 ...
随机推荐
- MVTMVC 区别
1,MVC的意思是 M:model V:views C:controller model是 主要是封装对数据库层的访问,对数据库中的数据进行增删改查操作 views 是 用于封装结果, 生程页面展示 ...
- Flutter——Dart Extension扩展方法的使用
dart的extension方法可以给已经存在的类添加新的函数,通过extension我们可以封装一些常用方法,提高开发效率. 例一:扩展String 给string添加一个log打印方法 exten ...
- PyQt(Python+Qt)学习随笔:QTabWidget选项卡部件的tabBar、count、indexOf方法
老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QTabWidget的每个选项卡都有一个对应的页面部件对象,可用通过count方法获取选项卡个数,可 ...
- PyQt(Python+Qt)学习随笔:Model/View中的枚举类 Qt.MatchFlag的取值及含义
老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 枚举类 Qt.MatchFlag描述在模型中搜索项时可以使用的匹配类型,它可以在QStandardI ...
- urllib.request.urlopen(req).read().decode解析http报文报“utf-8 codec can not decode”错处理
老猿前期执行如下代码时报"'utf-8' codec can't decode byte"错,代码及错误信息如下: >>> import urllib.reque ...
- 分布式计算框架-MapReduce 基本原理(MP用于分布式计算)
hadoop最主要的2个基本的内容要了解.上次了解了一下HDFS,本章节主要是了解了MapReduce的一些基本原理. MapReduce文件系统:它是一种编程模型,用于大规模数据集(大于1TB)的并 ...
- 第 7 篇 Scrum 冲刺博客
每天举行会议 会议照片: 昨天已完成的工作与今天计划完成的工作及工作中遇到的困难: 成员姓名 昨天完成工作 今天计划完成的工作 工作中遇到的困难 蔡双浩 补充注释,初步查找bug 修改bug 无 陈创 ...
- 【题解】P3631 [APIO2011]方格染色
很有意思的一道题,所以单独拿出来了. 完整分享看 这里 题目链接 luogu 题意 有一个包含 \(n \times m\) 个方格的表格.要将其中的每个方格都染成红色或蓝色.表格中每个 \(2 \t ...
- AcWing 361. 观光奶牛
01规划 设答案为 \(ans\). 二分答案,设当前二分值为 \(mid\). 设一个环 \(S\) 的边权为 \(t_1, t_2, t_3...\),点权为 \(f_1, f_2, f_3... ...
- Python不同包之间调用注意事项
1.不同包之间调用,因为在不同文件夹下,引用的时候加上包名就可以了.运行时,在eclipse直接运行没有问题.但是在,命令行运行时出现找不到模块的错误.原因是,Python只搜索当前目录和内置模块以及 ...