神刀安全网

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

关于死锁的原因

理解该死锁的 原因在于理解 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;             }         }     } 

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

大叔相关await&async的文章列表

C#~异步编程再续~await与async引起的w3wp.exe崩溃

C#~异步编程再续~async异步方法与同步方法的并行

基础才是重中之重~多线程的代价~我的内存都被吃了!

EF架构~EF异步改造之路~仓储接口的改造~续

EF架构~EF异步改造之路~让DbContextRepository去实现异步接口

EF架构~EF异步改造之路~仓储接口的改造

C#~异步编程续~.net4.5主推的await&async应用

C#~异步编程

感谢各位的阅读!

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » C#~异步编程再续~await与async引起的w3wp.exe崩溃-问题友好的解决

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址