C#~异步编程再续~await与async引起的w3wp.exe崩溃-问题友好的解决
发布日期:2021-08-25 00:12:03 浏览次数:2 分类:技术文章

本文共 5346 字,大约阅读时间需要 17 分钟。

关于死锁的原因

理解该死锁的原因在于理解await 处理contexts的方式,默认的,当一个未完成的Task 被await的时候,当前的上下文将在该Task完成的时候重新获得并继续执行剩余的代码。这个context就是当前的SynchronizationContext ,除非它是空的。WEB应用程序的SynchronizationContext 有排他性,只允许一个线程运行。当await 完成的时候,它试图在它原来的代码上下文执行它剩余的部分,但是该代码上下文中已经有一个线程在了,就是那个一直在同步等待async 完成的那个线程,它们两个相互等待,因此就死锁了。

MSDN使用异步应该注意

规则
描述
例外
避免使用 async void
优先使用 async Task 而不用 async void
Event handlers
Async到顶
不要混合使用 blocking 和 async 的代码
Console main method
注意配置好执行的context
尽量设置 ConfigureAwait(false)
需要context的除外

感恩的心

多谢网上很多文章的分享,在相关文章中找到了在同步代码中使用异步代码的无阻塞方案,之前也自己写了几个测试的DEMO,但Task<T>这种带有返回值的异步方法还是会出现死锁,之前代码如下:

///     /// 大叔测试    ///     public class tools    {        #region 假设这些方法被第三方被封装的,不可修改的方法        public static async Task TestAsync()        {            await Task.Delay(1000)                      .ConfigureAwait(false);//不会死锁        }        public static async Task
GetStrAsync() { return await Task.Run(() => "OK").ConfigureAwait(false); } public static async Task DelayTestAsync() { Logger.LoggerFactory.Instance.Logger_Info("DelayAsync"); await Task.Delay(1000); } public static async Task
DelayGetStrAsync() { return await Task.Run(() => "OK"); } #endregion #region 我们需要在自己代码中封装它,解决线程死锁 ///
/// 没有返回值的同步调用异步的实体 /// ///
public static void ForWait(Func
func) { func().ConfigureAwait(false); } ///
/// 存在返回值的同步调用异步的实体 /// ///
///
///
public static T ForResult
(Func
> func) { var a = func(); a.ConfigureAwait(false); return a.Result; } #endregion }

对于上面的代码,当执行一个Task反回类型(即无返回结果)时,程序是没有问题的,但可以存在返回结果,那么上面的ForResult方法依旧会产生死锁!执着的我当然不会就此罢休,找了一些文章后,终于还是有了结果,在对当前上下文和异步上下文进行了简

单的处理后,最终还是实现了同步与异步的并存所以说,人是最聪明的一种动物,一切都皆有可能,只要你想!

Lind.DDD.Utils.AsyncTaskManager代码如下,希望可以给大家带来一些启发和帮助

///     /// 异步线程管理-在同步程序中调用异步,解决了线程死锁问题    ///     public class AsyncTaskManager    {        ///         /// 运行无返回类型的异步方法        ///         ///         public static void RunSync(Func
task) { var oldContext = SynchronizationContext.Current;//同步上下文 var synch = new ExclusiveSynchronizationContext();//异步上下文 SynchronizationContext.SetSynchronizationContext(synch);//设置当前同步上下文 synch.Post(async obj => { try { await task(); } catch (Exception e) { synch.InnerException = e; throw; } finally { synch.EndMessageLoop(); } }, null); synch.BeginMessageLoop(); SynchronizationContext.SetSynchronizationContext(oldContext); } ///
/// 运行返回类型为T的异步方法 /// ///
///
///
public static T RunSync
(Func
> task) { var oldContext = SynchronizationContext.Current; var synch = new ExclusiveSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(synch); T ret = default(T);//动作的默认值 synch.Post(async obj => { try { ret = await task(); } catch (Exception e) { synch.InnerException = e; throw; } finally { synch.EndMessageLoop(); } }, null); synch.BeginMessageLoop(); SynchronizationContext.SetSynchronizationContext(oldContext); return ret; } ///
/// 异步上下文对象 /// class ExclusiveSynchronizationContext : SynchronizationContext { private bool done; public Exception InnerException { get; set; } readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false); readonly Queue
> items = new Queue
>(); public override void Send(SendOrPostCallback d, object state) { throw new NotSupportedException("We cannot send to our same thread"); } ///
/// 添加到异步队列 /// ///
///
public override void Post(SendOrPostCallback d, object state) { lock (items) { items.Enqueue(Tuple.Create(d, state)); } workItemsWaiting.Set(); } ///
/// 异步结束 /// public void EndMessageLoop() { Post(obj => done = true, null); } ///
/// 处理异步队列中的消息 /// public void BeginMessageLoop() { while (!done) { Tuple
task = null; lock (items) { if (items.Count > 0) { task = items.Dequeue(); } } if (task != null) { task.Item1(task.Item2); if (InnerException != null) // the method threw an exeption { throw new AggregateException("AsyncInline.Run method threw an exception.", InnerException); } } else { workItemsWaiting.WaitOne(); } } } public override SynchronizationContext CreateCopy() { return this; } } }

最后我们进行测试中,看到线程没有出现死锁问题!

感谢各位的阅读!

本文转自博客园张占岭(仓储大叔)的博客,原文链接:C#~异步编程再续~await与async引起的w3wp.exe崩溃-问题友好的解决,如需转载请自行联系原博主。

转载地址:https://blog.csdn.net/weixin_33836223/article/details/90187025 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:[LeetCode] Ransom Note 赎金条
下一篇:DFS中的奇偶剪枝学习笔记

发表评论

最新留言

表示我来过!
[***.240.166.169]2024年03月31日 19时56分28秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

【Leetcode刷题篇】leetcode32 最长有效括号 2019-04-26
【Leetcode刷题篇】leetcode128 最长连续序列 2019-04-26
【Leetcode刷题篇】leetcode72 编辑距离 2019-04-26
【Leetcode刷题篇】leetcode312 戳气球 2019-04-26
前后端分离如何使用spring boot处理跨域请求 2019-04-26
【Leetcode刷题篇】leetcode283 移动零 2019-04-26
【Leetcode刷题篇】leetcode611 有效三角形的个数 2019-04-26
【Leetcode刷题篇】leetcode26 删除排序数组中的重复项 2019-04-26
【大话Java面试】-如何通俗易懂的理解Redis的分布式寻址算法hash slot? 2019-04-26
【大话Java面试】-如何通俗易懂的理解单例模式? 2019-04-26
【大话Java面试】请列出Java中几个常用的设计模式? 2019-04-26
【大话Java面试】-如何通俗易懂的理解Java异常以及Java异常处理? 2019-04-26
【大话Mysql面试】-Mysql的索引为什么要使用B+树,而不是B树,红黑树等之类? 2019-04-26
【大话Mysql面试】-如何通俗易懂的了解Mysql的索引最左前缀匹配原则 2019-04-26
【大话Mysql面试】-MYSQL的两种存储引擎MyISAM与InnoDB的区别是什么? 2019-04-26
【大话Mysql面试】-InnoDB可重复读隔离级别下如何避免幻读?MVCC和next-key是什么 2019-04-26
【大话Mysql面试】-Mysql如何恢复数据?如何进行主从复制?Binlog日志到底是什么? 2019-04-26
理解String.intern()和String类常量池疑难解析例子 2019-04-26
python flask打造前后端分离的口罩检测 2019-04-26
【大话Mysql面试】-MySQL基础知识 2019-04-26