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

简介:
最近怪事又开始发生了,IIS的应用程序池无做挂掉,都指向同一个矛头,async,threadPool,Task,还有一个System.NullReferenceException,所以这些都让我们感觉,我们的异步程序出现了问题,事实也是如此,我们的异步调用引用了对“上下文”的非空引用,最后导致w3wp进程死掉!
通过其它前辈的分享,找到了问题产生的原因,大叔也总结一下
1 async方法需要使用await等待它的结果,这样可以保证你的SynchronizationContext上下文不为空,即不会出现非空引用的错误。
2 在调用async方法时,如果不方法加await关键字,也可以使用它的ConfigureAwait(false)方法,它虽然不会保存SynchronizationContext上下文,但它也不会报非空引用的错误。
3 在一个新线程里调用async的异步方法,需要我们注意上面两点
参看文章
http://www.cnblogs.com/cmt/p/configure_await_false.html
http://www.cnblogs.com/cmt/p/sokcet_memory_leak.html
技术点说明
1 Task.Run(()=>{}); 将一个任务添加到线程池里,排队执行
2 async 标识一个方法为异步方法,可以与主线程并行执行,发挥CPU的多核优势
3 await 在调用一个async方法前可以添加这个修饰符,它意思是等待当前异步方法执行完后,再执行下面的代码
4 ConfigureAwait(true),代码由同步执行进入异步执行时,当前线程上下文信息就会被捕获并保存至 SynchronizationContext中,供异步执行中使用,并且供异步执行完成之后的同步执行中使用
5 Configurewait(flase),不进行线程上下文信息的捕获,async方法中与await之后的代码执行时就无法获取await之前的线程的上下文信息,在ASP.NET中最直接的影响就是HttpConext.Current的值为null,但不会出现非空引用的错误
Async引起的死锁,w3wp.exe挂的原因
对于将异步方法偷懒的人,即使用Wait()和Result的人,将会为些付出代价,因为它会引起线程的死锁,最终导致w3wp挂掉,注意在控制器console程序中,这件事不会发生。
MSDN:https://msdn.microsoft.com/zh-cn/magazine/jj991977.aspx
始终使用 Async
异步代码让我想起了一个故事,有个人提出世界是悬浮在太空中的,但是一个老妇人立即提出质疑,她声称世界位于一个巨大乌龟的背上。 当这个人问乌龟站在哪里时,老夫人回答:“很聪明,年轻人,下面是一连串的乌龟!”在将同步代码转换为异步代码时,您会发现,如果异步代码调用其他异步代码并且被其他异步代码所调用,则效果最好 — 一路向下(或者也可以说“向上”)。 其他人已注意到异步编程的传播行为,并将其称为“传染”或将其与僵尸病毒进行比较。 无论是乌龟还是僵尸,无可置疑的是,异步代码趋向于推动周围的代码也成为异步代码。 此行为是所有类型的异步编程中所固有的,而不仅仅是新 async/await 关键字。
“始终异步”表示,在未慎重考虑后果的情况下,不应混合使用同步和异步代码。 具体而言,通过调用 Task.Wait 或 Task.Result 在异步代码上进行阻塞通常很糟糕。 对于在异步编程方面“浅尝辄止”的程序员,这是个特别常见的问题,他们仅仅转换一小部分应用程序,并采用同步 API 包装它,以便代码更改与应用程序的其余部分隔离。 不幸的是,他们会遇到与死锁有关的问题。 在 MSDN 论坛、Stack Overflow 和电子邮件中回答了许多与异步相关的问题之后,我可以说,迄今为止,这是异步初学者在了解基础知识之后最常提问的问题: “为何我的部分异步代码死锁?”
其中一个方法发生阻塞,等待 async 方法的结果。 此代码仅在控制台应用程序中工作良好,但是在从 GUI 或 ASP.NET 上下文调用时会死锁。 此行为可能会令人困惑,尤其是通过调试程序单步执行时,这意味着没完没了的等待。 在调用 Task.Wait 时,导致死锁的实际原因在调用堆栈中上移。
这种死锁的根本原因是 await 处理上下文的方式。 默认情况下,当等待未完成的 Task 时,会捕获当前“上下文”,在 Task 完成时使用该上下文恢复方法的执行。 此“上下文”是当前 SynchronizationContext(除非它是 null,这种情况下则为当前 TaskScheduler)。 GUI 和 ASP.NET 应用程序具有 SynchronizationContext,它每次仅允许一个代码区块运行。 当 await 完成时,它会尝试在捕获的上下文中执行 async 方法的剩余部分。 但是该上下文已含有一个线程,该线程在(同步)等待 async 方法完成。 它们相互等待对方,从而导致死锁。
请注意,控制台应用程序不会形成这种死锁。 它们具有线程池 SynchronizationContext 而不是每次执行一个区块的 SynchronizationContext,因此当 await 完成时,它会在线程池线程上安排 async 方法的剩余部分。 该方法能够完成,并完成其返回任务,因此不存在死锁。 当程序员编写测试控制台程序,观察到部分异步代码按预期方式工作,然后将相同代码移动到 GUI 或 ASP.NET 应用程序中会发生死锁,此行为差异可能会令人困惑。
此问题的最佳解决方案是允许异步代码通过基本代码自然扩展。 如果采用此解决方案,则会看到异步代码扩展到其入口点(通常是事件处理程序或控制器操作)。 控制台应用程序不能完全采用此解决方案,因为 Main 方法不能是 async。 如果 Main 方法是 async,则可能会在完成之前返回,从而导致程序结束。  控制台应用程序的 Main 方法是代码可以在异步方法上阻塞为数不多的几种情况之一。
代码如下
复制代码
 public class tools
    {
        public static async Task TestAsync()
        {
            await Task.Delay(1000);
        }
    }
    public class HomeController : Controller
    {

        public ActionResult Index()
        {
            tools.TestAsync().Wait();//产生死锁,w3wp.exe挂掉
            ViewBag.Message = "test";
            return View();
        }
}
复制代码
 在Task.Delay(1000)后面添加Configurewait(flase)可以有效的避免代码的死锁!( 此时,当等待完成时,它会尝试在线程池上下文中执行 async 方法的剩余部分。 该方法能够完成,并完成其返回任务,因此不存在死锁。 如果需要逐渐将应用程序从同步转换为异步,则此方法会特别有用。)
以上就是我们在解决由异步引起的w3wp.exe崩溃中所学习到的知识!
感谢各位的阅读
本文转自博客园张占岭(仓储大叔)的博客,原文链接: C#~异步编程再续~await与async引起的w3wp.exe崩溃,如需转载请自行联系原博主。
目录
相关文章
|
3月前
|
编译器 数据处理 C#
C#中的异步流:使用IAsyncEnumerable<T>和await foreach实现异步数据迭代
【1月更文挑战第10天】本文介绍了C#中异步流的概念,并通过使用IAsyncEnumerable<T>接口和await foreach语句,详细阐述了如何异步地迭代数据流。异步流为处理大量数据或需要流式处理数据的场景提供了一种高效且非阻塞性的方法,使得开发者能够更优雅地处理并发和数据流问题。
|
3月前
|
C# 开发者
深入理解C#中的`Task<T>`:异步编程的核心
【1月更文挑战第3天】本文旨在探讨C#中`Task<T>`的使用和理解,作为异步编程模式的核心组件。`Task<T>`允许开发者在不阻塞主线程的情况下执行异步操作,并返回一个指定类型`T`的结果。通过定义返回`Task<T>`的异步方法、使用`async`和`await`关键字、处理异常以及获取任务结果,开发者可以编写出高效且响应迅速的应用程序。此外,本文还介绍了如何配置任务以及实现任务的连续性和组合,为掌握C#中的异步编程提供了全面的指导。
|
1月前
|
C#
C#学习系列相关之多线程(四)----async和await的用法
C#学习系列相关之多线程(四)----async和await的用法
|
8月前
|
C#
C#异步编程
C#异步编程
164 0
|
10月前
|
C#
c#异步编程
c#异步编程原理,await asnyc的使用方法。异步编程是指在程序执行过程中,不需要等待某个操作完成,就可以继续执行后续的代码。
267 0
|
C#
C#中await和async关键字的简单理解
C#中await和async关键字的简单理解
45 0
|
C#
C#网络应用编程,实验七: 异步编程练习
C#网络应用编程,实验七: 异步编程练习
94 1
C#网络应用编程,实验七: 异步编程练习
|
C#
C#网络应用编程,异步编程基础练习
C#网络应用编程,异步编程基础练习
124 1
C#网络应用编程,异步编程基础练习
C#多线程(18):一篇文章就理解async和await
C#多线程(18):一篇文章就理解async和await
222 0
C#多线程(18):一篇文章就理解async和await
|
1月前
|
C#
24. C# 编程:用户设定敌人初始血值的实现
24. C# 编程:用户设定敌人初始血值的实现
22 0