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一个异步方法的更多相关文章

  1. 【转】 C#中Finally的一个不太常见的用法

    原文地址:http://www.cnblogs.com/listhome/p/3664300.html 最近在看.net BCL 传送门 的源码. 在 System.Collections.Concu ...

  2. C#中Finally的一个不太常见的用法

    最近在看.net BCL 传送门 的源码. 在 System.Collections.Concurrent.ConcurrentQueue 中看到一段有意思的代码.注意这段代码是写在Concurren ...

  3. 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,计算可以从中找到 ...

  4. SpriteBuilder代码中弱引用(weak)需要注意的地方

    比如在GameScene类中有一个弹出菜单层实例的引用,我们有: @implementation GameScene{ //other ivars __weak GameMenuLayer *_pop ...

  5. iOS中如何创建一个滑出式导航面板(1)

    本文将介绍如何创建类似Facebook和Path iOS程序中的滑出式导航面板. 向右滑动 滑出式设计模式可以让开发者在程序中添加常用的导航功能,又不会浪费屏幕上宝贵的空间.用户可以在任意时间滑出导航 ...

  6. Spring Boot中只能有一个WebMvcConfigurationSupport配置类

    首先将结论写文章的最前面,一个项目中只能有一个继承WebMvcConfigurationSupport的@Configuration类(使用@EnableMvc效果相同),如果存在多个这样的类,只有一 ...

  7. java高级用法之:在JNA中将本地方法映射到JAVA代码中

    目录 简介 Library Mapping Function Mapping Invocation Mapping 防止VM崩溃 性能考虑 总结 简介 不管是JNI还是JNA,最终调用的都是nativ ...

  8. 实际案例:在现有代码中通过async/await实现并行

    一项新技术或者一个新特性,只有你用它解决实际问题后,才能真正体会到它的魅力,真正理解它.也期待大家能够多分享解一些解决实际问题的内容. 在我们遭遇“黑色30秒”问题的过程中,切身体会到了异步的巨大作用 ...

  9. 在现有代码中通过async/await实现并行

    在现有代码中通过async/await实现并行 一项新技术或者一个新特性,只有你用它解决实际问题后,才能真正体会到它的魅力,真正理解它.也期待大家能够多分享解一些解决实际问题的内容. 在我们遭遇“黑色 ...

随机推荐

  1. javascript开发后端程序的神器nodejs

    目录 简介 nodejs的历史 nodejs简介 nodejs的运行环境 process 终止进程 env argv CLI交互 exports模块 nodejs API nodejs的框架 简介 j ...

  2. 编写测试用例 QQ账号6--10位自然数 某城市电话号码 126邮箱注册功能

  3. flask:蓝图--blueprint

    一.蓝图 1.什么是蓝图?(WHAT) 1)蓝图就是模块化处理的类 2)用于实现单个应用的视图.模板.静态文件的集合 总结:蓝图就是一个存储操作路由映射方法的容器,主要用来实现客户端请求和URL相互关 ...

  4. 自学linux——20.Samba服务器的搭建

    Samba服务器的搭建 一.Samba的认识 1.Samba是一个能让Linux系统应用Microsoft网络通讯协议的软件 2.Samba不仅用于Linux与windows系统直接的文件共享和打印共 ...

  5. moviepy音视频剪辑VideoClip类fl_image方法及参数image_func的功能介绍

    ☞ ░ 前往老猿Python博文目录 ░ moviepy音视频剪辑模块的视频剪辑基类VideoClip的fl_image方法用于进行对剪辑帧数据进行变换. 调用语法:fl_image(self, im ...

  6. 快速理解Python中使用百分号占位符的字符串格式化方法中%s和%r的输出内容的区别

    <Python中使用百分号占位符的字符串格式化方法中%s和%r的输出内容有何不同?>老猿介绍了二者的区别,为了快速理解,老猿在此使用另外一种方式补充说明一下: 1.使用%r是调用objec ...

  7. 超详细讲解mysql存储过程中的in/out/inout

    存储过程 大概定义:用一个别名来描述多个sql语句的执行过程. 最简单 delimiter // create PROCEDURE p1() begin select * from userinfo; ...

  8. Nmap学习使用指南

    本博客严重参考 Nmap使用指南1.0: https://github.com/scanfsec/penetration/blob/master/Nmap%E4%BD%BF%E7%94%A8%E6%8 ...

  9. js- 实现属性名的拼接 obj['name']

     obj.name---->obj[name] 这两种调用方式一样,使用obj.name内部转换成 obj['name'], 使用obj['name']更快. obj['name'] 里面必须是 ...

  10. BJOI2015 隐身术

    落谷. Description 给你两个串 \(A.B\).询问 \(B\) 中有多少个非空子串和 \(A\) 的编辑距离不超过 \(K\). Solution 发现 \(K \le 5\),考虑可以 ...