专门做项目代理的网站,wordpress title 竖线,seo培训机构排名,wordpress登录页面修改一#xff1a;背景
1. 讲故事
最近聊了不少和异步相关的话题#xff0c;有点疲倦了#xff0c;今天再写最后一篇作为近期这类话题的一个封笔吧#xff0c;下篇继续写我熟悉的 生产故障 系列#xff0c;突然亲切感油然而生#xff0c;哈哈#xff0c;免费给别人看程序故…一背景
1. 讲故事
最近聊了不少和异步相关的话题有点疲倦了今天再写最后一篇作为近期这类话题的一个封笔吧下篇继续写我熟悉的 生产故障 系列突然亲切感油然而生哈哈免费给别人看程序故障是一种积阴德阳善的事情欲知前世因今生受者是。欲知来世果今生做者是。
在任务延续方面我个人的总结就是三类分别为
StateMachineContinueWithAwaiter
话不多说我们逐个研究下底层是咋玩的
二异步任务延续的玩法
1. StateMachine
说到状态机大家再熟悉不过了也是 async,await 的底层化身很多人看到 async await 就想到了IO场景其实IO场景和状态机是两个独立的东西状态机是一种设计模式把这个模式套在IO场景会让代码更加丝滑仅此而已。为了方便讲述我们写一个 StateMachine 与 IO场景 无关的一段测试代码。
internal class Program{static void Main(string[] args){UseAwaitAsync();Console.ReadLine();}static async Taskstring UseAwaitAsync(){var html await Task.Run(() {Thread.Sleep(1000);var response htmlh1博客园/h1/html;return response;});Console.WriteLine($GetStringAsync 的结果{html});return html;}}
那这段代码在底层是如何运作的呢刚才也说到了asyncawait只是迷惑你的一种幻象我们必须手握辟邪宝剑斩开幻象显真身这里借助 ilspy 截图如下 从卦中看本质上就是借助AsyncTaskMethodBuilderstring 建造者将 awaiter 和 stateMachine 做了一个绑定感兴趣的朋友可以追一下 AwaitUnsafeOnCompleted() 方法最后状态机 UseAwaitAsyncd__1 实例会放入到 Task.Run 的 m_continuationObject 字段。如果有朋友对流程比较蒙的话我画了一张简图。 图和代码都有了接下来就是眼见为实。分别在 AddTaskContinuation 和 RunContinuations 方法中做好埋点前者可以看到 延续任务 是怎么加进去的后者可以看到 延续任务 是怎么取出来的。 心细的朋友会发现这卦上有一个很特别的地方就是 allowInliningtrue也就是回调函数StateMachine是在当前线程上一撸到底的。
有些朋友可能要问能不能让延续任务 跑在单独线程上 可以是可以但你得把 Task.Run 改成 Task.Factory.StartNew 这样就可以设置TaskCreationOptions参数参考代码如下 var html await Task.Factory.StartNew(() {}, TaskCreationOptions.RunContinuationsAsynchronously);2. ContinueWith
那些同处于被裁的35岁大龄程序员应该知道Task是 framework 4.0 时代出来的而async,await是4.5出来的所以在这个过渡期中有大量的项目会使用ContinueWith 导致回调地狱。。。 这里我们对比一下两者有何不同先写一段参考代码。
internal class Program{static void Main(string[] args){UseContinueWith();Console.ReadLine();}static Taskstring UseContinueWith(){var query Task.Run(() {Thread.Sleep(1000);var response htmlh1博客园/h1/html;return response;}).ContinueWith(t {var html t.Result;Console.WriteLine($GetStringAsync 的结果{html});return html;});return query;}}
从卦代码看确实没有asyncawait简洁那 ContinueWith 内部做了什么呢感兴趣的朋友可以跟踪一下本质上和 StateMachine 的玩法是一样的都是借助 m_continuationObject 来实现延续画个简图如下 代码和模型图都有了接下来就是用 dnspy 开干了。。。还是在 AddTaskContinuation 和 RunContinuations 上埋伏断点观察。 从卦中可以看到延续任务使用新线程来执行的并没有一撸到底这明显与 asyncawait 的方式不同有些朋友可能又要说了那如何实现和StateMachine一样的呢这就需要在 ContinueWith 中新增 ExecuteSynchronously 同步参数参考如下 var query Task.Run(() { }).ContinueWith(t {}, TaskContinuationOptions.ExecuteSynchronously);3. Awaiter
使用Awaiter做任务延续的朋友可能相对少一点它更多的是和 StateMachine 打配合当然单独使用也可以但没有前两者灵活它更适合那些不带返回值的任务延续,本质上也是借助 m_continuationObject 字段实现的一套底层玩法话不多说上一段代码
static Taskstring UseAwaiter(){var awaiter Task.Run(() {Thread.Sleep(1000);var response htmlh1博客园/h1/html;return response;}).GetAwaiter();awaiter.OnCompleted(() {var html awaiter.GetResult();Console.WriteLine($UseAwaiter 的结果{html});});return Task.FromResult(string.Empty);}
前面两种我配了图这里没有理由不配了哈哈模型图如下 接下来把程序运行起来观察截图 从卦中观察它和StateMachine一样默认都是 一撸到底 的方式。
三RunContinuations 观察
这一小节我们单独说一下 RunContinuations 方法因为这里的实现太精妙了不幸的是Dnspy和ILSpy反编译出来的代码太狗血原汁原味的简化后代码如下 private void RunContinuations(object continuationObject) // separated out of FinishContinuations to enable it to be inlined{bool canInlineContinuations (m_stateFlags (int)TaskCreationOptions.RunContinuationsAsynchronously) 0 RuntimeHelpers.TryEnsureSufficientExecutionStack();switch (continuationObject){// Handle the single IAsyncStateMachineBox case. This could be handled as part of the ITaskCompletionAction// but we want to ensure that inlining is properly handled in the face of schedulers, so its behavior// needs to be customized ala raw Actions. This is also the most important case, as it represents the// most common form of continuation, so we check it first.case IAsyncStateMachineBox stateMachineBox:AwaitTaskContinuation.RunOrScheduleAction(stateMachineBox, canInlineContinuations);LogFinishCompletionNotification();return;// Handle the single Action case.case Action action:AwaitTaskContinuation.RunOrScheduleAction(action, canInlineContinuations);LogFinishCompletionNotification();return;// Handle the single TaskContinuation case.case TaskContinuation tc:tc.Run(this, canInlineContinuations);LogFinishCompletionNotification();return;// Handle the single ITaskCompletionAction case.case ITaskCompletionAction completionAction:RunOrQueueCompletionAction(completionAction, canInlineContinuations);LogFinishCompletionNotification();return;}}
卦中的 case 挺有意思的除了本篇聊过的 TaskContinuation 和 IAsyncStateMachineBox 之外还有另外两种 continuationObject这里说一下 ITaskCompletionAction 是怎么回事其实它是 Task.Result 的底层延续类型所以大家应该能理解为什么 Task.Result 能唤醒主要是得益于Task.m_continuationObject completionAction 所致。
说了这么说如何眼见为实呢可以从源码中寻找答案。
private bool SpinThenBlockingWait(int millisecondsTimeout, CancellationToken cancellationToken){var mres new SetOnInvokeMres();AddCompletionAction(mres, addBeforeOthers: true);var returnValue mres.Wait(Timeout.Infinite, cancellationToken);}private sealed class SetOnInvokeMres : ManualResetEventSlim, ITaskCompletionAction{internal SetOnInvokeMres() : base(false, 0) { }public void Invoke(Task completingTask) { Set(); }public bool InvokeMayRunArbitraryCode false;}
从卦中可以看到其实就是把 ITaskCompletionAction 接口的实现类 SetOnInvokeMres 塞入了 Task.m_continuationObject 中一旦Task执行完毕之后就会调用 Invoke() 下的 Set() 来实现事件唤醒。
四总结
虽然异步任务延续有三种实现方法但底层都是一个套路即借助 Task.m_continuationObject 字段玩出的各种花样当然他们也是有一些区别的即对 m_continuationObject 任务是否用单独的线程调度产生了不同的意见分歧。