Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 610|回复: 0

C#~异步编程再续~await与async引起的w3wp.exe崩溃-问题友好的解决

[复制链接]
  • TA的每日心情
    奋斗
    2025-3-18 14:43
  • 签到天数: 805 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    73万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    731050
    发表于 2021-6-6 05:50:30 | 显示全部楼层 |阅读模式

    返回目录

    关于死锁的原因

    理解该死锁的原因在于理解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>这种带有返回值的异步方法还是会出现死锁,之前代码如下:

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

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

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

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

     

      /// <summary>
        /// 异步线程管理-在同步程序中调用异步,解决了线程死锁问题
        /// </summary>
        public class AsyncTaskManager
        {
            /// <summary>
            /// 运行无返回类型的异步方法
            /// </summary>
            /// <param name="task"></param>
            public static void RunSync(Func<Task> 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);
            }
            /// <summary>
            /// 运行返回类型为T的异步方法
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="task"></param>
            /// <returns></returns>
            public static T RunSync<T>(Func<Task<T>> 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;
            }
    
            /// <summary>
            /// 异步上下文对象
            /// </summary>
            class ExclusiveSynchronizationContext : SynchronizationContext
            {
                private bool done;
                public Exception InnerException { get; set; }
                readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
                readonly Queue<Tuple<SendOrPostCallback, object>> items =
                 new Queue<Tuple<SendOrPostCallback, object>>();
    
                public override void Send(SendOrPostCallback d, object state)
                {
                    throw new NotSupportedException("We cannot send to our same thread");
                }
                /// <summary>
                /// 添加到异步队列
                /// </summary>
                /// <param name="d"></param>
                /// <param name="state"></param>
                public override void Post(SendOrPostCallback d, object state)
                {
                    lock (items)
                    {
                        items.Enqueue(Tuple.Create(d, state));
                    }
                    workItemsWaiting.Set();
                }
                /// <summary>
                /// 异步结束
                /// </summary>
                public void EndMessageLoop()
                {
                    Post(obj => done = true, null);
                }
                /// <summary>
                /// 处理异步队列中的消息
                /// </summary>
                public void BeginMessageLoop()
                {
                    while (!done)
                    {
                        Tuple<SendOrPostCallback, object> 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;
                }
            }
        }

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

    感谢各位的阅读!

    返回目录

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-4-21 06:56 , Processed in 0.061677 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表