

在 Emit 代码中如何await一个异步方法
source link: http://www.cnblogs.com/fs7744/p/14220947.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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
Recommend
-
56
前言 异步编程模式在前端开发过程中,显得越来越重要。从最开始的XHR到封装后的Ajax都在试图解决异步编程过程中的问题。随着ES6新标准的到来,处理异步数据流又有了新的方案。我们都知道,在传统的ajax请求中,当异步请求之间的数据存在依赖关系的时候,就可能产生...
-
28
关于Emit的博客已经进入第四篇,在读本篇博文之前,我希望读者能先仔细回顾博主之前所编写的关于Emit的博文,从该篇博文开始,我们就可以真正的使用Emit,并把知识转化为实战,我也会把之前的博文链接放在下方,以方便读者阅读,大家也可...
-
17
Dart学习笔记(八):异步支持与async/await 作者: RuphiLau 时间: July 4, 2019 分类:
-
12
.NET Web应用中为什么要使用async/await异步编程?前言1.什么是async/aw...
-
16
Conversation Copy link Contributor ...
-
4
Copy link Member
-
11
Copy link Member eddyb commented...
-
13
“Why do some objects emit a ‘pitchless’ sound when hit, and others a 'tuned’ sound?”...
-
9
如果您已经学习 JavaScript 一段时间了,那么您之前可能听说过“异步”这个术语。 这是因为 JavaScript 是一种异步语言……但这到底意味着什么?在本文中,我希望向您展示这个概念并不像听起来那么困...
-
6
异步这个概念在不同语境下有不同的解释,比如在一个单核CPU里开启两个线程执行两个函数,通常认为这种调用是异步的,但对于CPU来说它是单核不可能同时运行两个函数,不过是由系统调度在不同的时间分片中执行。一般来说,如果两个工作能同时进行,就认为是异步的。在...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK