返回目录html
最近怪事又开始发生了,IIS的应用程序池无作挂掉,都指向同一个矛头,async,threadPool,Task,还有一个System.NullReferenceException,因此这些都让咱们感受,咱们的异步程序出现了问题,事实也是如此,咱们的异步调用引用了对“上下文”的非空引用,最后致使w3wp进程死掉!
经过其它前辈的分享,找到了问题产生的缘由,大叔也总结一下
1 async方法须要使用await等待它的结果,这样能够保证你的SynchronizationContext上下文不为空,即不会出现非空引用的错误。程序员
2 在调用async方法时,若是不方法加await关键字,也可使用它的ConfigureAwait(false)方法,它虽然不会保存SynchronizationContext上下文,但它也不会报非空引用的错误。编程
3 在一个新线程里调用async的异步方法,须要咱们注意上面两点app
参看文章异步
http://www.cnblogs.com/cmt/p/configure_await_false.htmlasync
http://www.cnblogs.com/cmt/p/sokcet_memory_leak.html异步编程
技术点说明学习
1 Task.Run(()=>{}); 将一个任务添加到线程池里,排队执行测试
2 async 标识一个方法为异步方法,能够与主线程并行执行,发挥CPU的多核优点ui
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崩溃中所学习到的知识!
感谢各位的阅读!