在 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实现并行 一项新技术或者一个新特性,只有你用它解决实际问题后,才能真正体会到它的魅力,真正理解它.也期待大家能够多分享解一些解决实际问题的内容. 在我们遭遇“黑色 ...
随机推荐
- Docker实战 | 第二篇:IDEA集成Docker插件实现一键自动打包部署微服务项目,一劳永逸的技术手段值得一试
一. 前言 大家在自己玩微服务项目的时候,动辄十几个服务,每次修改逐一部署繁琐不说也会浪费越来越多时间,所以本篇整理通过一次性配置实现一键部署微服务,实现真正所谓的一劳永逸. 二. 配置服务器 1. ...
- webpack系列:webpack小老弟接了个简单活
webpack深入浅出系列:进阶篇 前沿,本篇文章的讲解思路是以webpack的五大核心为线索,以webpack对象为第一视角来讲述(以前记得看过一个文笔非常厉害的技术啊婆写的,非常有趣.然后我就想着 ...
- 九. Vuex详解
1. 理解Vuex 1.1 Vuex功能 官方解释 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用 集中式存储 管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方 ...
- PyQt(Python+Qt)学习随笔:QTreeWidget树型部件中的QTreeWidgetItem项构造方法
老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QTreeWidget树型部件的项是单独的类对象,这个类就是QTreeWidgetItem. QTr ...
- PyQt(Python+Qt)学习随笔:图例解释QFrame类的lineWidth、midLineWidth以及frameWidth属性
老猿Python博文目录 老猿Python博客地址 QFrame类有四个跟宽度相关的属性,分别是width.lineWidth.midLineWidth以及frameWidth属性.width是整个Q ...
- EF优缺点解析
原先用的是三层架构中ADO.NET做底层开发,纯手工sql语句拼装.后来遇到一个MVC+EF项目,体会到了EF的强大性. 它是微软封装好一种ADO.NET数据实体模型,将数据库结构以ORM模式映射到应 ...
- js防抖与节流了解一下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Codeforces Edu Round 64 A-D
A. Inscribed Figures 分类讨论打表即可. PS:这道题翻译有歧义. 这样稍微翻转一下,就可以是\(7\)个交点呀...(大概是我没看英文题干导致的惨案) #include < ...
- 题解-Infinite Path
题解-Infinite Path \(\color{#9933cc}{\texttt{Infinite Path}}\) \(T\) 组测试数据.每次给你一个 \(n\) 的排列 \(\{p_n\}\ ...
- 微信端video去除最顶层播放
https://x5.tencent.com/tbs/guide/video.html 给video标签添加属性 x5-video-player-type="h5"