好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

IIS应用程序池崩溃

IIS应用程序池崩溃

System.Threading.Tasks.Task引起的IIS应用程序池崩溃

 

问题现象

IIS应用程序池崩溃(Crash)的特征如下:

1. 从客户端看,浏览器一直处于连接状态,Web服务器无响应。

2. 从服务器端看(Windows Server 2008 + IIS 7.0),在事件日志中会出现Event ID为5010的错误:

A process serving application pool 'q.cnblogs.com' failed to respond to a ping. The process id was '20080'.

这个错误的意思是:IIS检测到程序池'q.cnblogs.com'无响应。为什么没有响应呢?因为程序池'q.cnblogs.com'崩溃了。然后呢?IIS会强制回收应用程序池。

(注:如果在你的Web服务器的事件日志中出现这个错误,一定是某个原因引起了应用程序池崩溃。)

问题原因

我们这次遇到的应用程序池崩溃,是由于在使用System.Threading.Tasks.Task进行异步操作时产生了未处理的异常。

示例代码如下:

Task.Factory.StartNew(() =>
{
// 下面的代码未用try..catch捕获异常
// ...
});

复制代码

注:这是一个不需要Callback的异步操作,后续没有task.wait(或者静态方法Task.WaitAll或Task.WaitAny)操作。

当时我们发布程序后,由于Task中代码产生了异常,整个站点无法正常访问,程序池一直处于“崩溃->回收->崩溃->回收”的循环。

解决方法

捕获Task中所有代码的异常,示例代码如下:

Task.Factory.StartNew(() =>
{
try
{
// ...
}
catch { }
});

复制代码

问题分析

在stackoverflow上提到了 这个问题的原因 :

If you create a Task, and you don't ever call  task.Wait()  or try to retrieve the result of a  Task<T> , when the task is collected by the garbage collector, it will tear down your application during finalization. For details, see MSDN's page on  Exception Handling in the TPL .

The best option here is to "handle" the exception. 

根据上面的英文,我的理解是:当你创建一个Task,没有调用过task.Wait()或者没有获取它的执行结果,(如果Task中出现了未处理的异常),当这个Task被GC回收时,在GC finalization阶段,会让当前应用程序崩溃。

进一步看MSDN中的 Exception Handling (Task Parallel Library) :

"Unhandled exceptions that are thrown by user code that is running inside a task are propagated back to the joining thread. ···Exceptions are propagated when you use one of the static or instance Task.Wait or Task(Of TResult).Wait methods···"

翻译:在一个task中运行的代码抛出的未处理异常会被回传给(创建该task的)主线程。···当你调用Task.Wait时,异常才会被回传(给主线程)。

分析:当我们遇到的情况是没调用Task.Wait,也就是异常没有被回传给主线程。下面的这句就提到了这个:

"If you do not wait on a task that propagates an exception, or access its Exception property, the exception is escalated according to the .NET exception policy when the task is garbage-collected."

译:如果你在一个task中没有等待异常被传播,或者访问它的异步特性,在task被GC回收时,该异常会遵循.NET异常策略被逐步升级。

分析:逐步升级的后果就是当前应用程序进程崩溃,对于ASP.NET程序来说,就是应用程序池崩溃。

进一步的解决方法

MSDN上的推荐做法是用Task.ContinueWith观察Task中抛出的异常并进行处理,示例代码如下:

 var  task1 = Task.Factory.StartNew(() =>
{
throw new MyCustomException( " Task1 faulted. " );
})
.ContinueWith((t) =>
{
Console.WriteLine( " I have observed a {0} " ,
t.Exception.InnerException.GetType().Name);
},
TaskContinuationOptions.OnlyOnFaulted);

复制代码

小结

应用程序池崩溃的原因总结 —— System.Threading.Tasks.Task中的代码抛出了未处理的异常,由于没有Task.Wait()操作,异常没有被回传给主线程,在GC回收时,发现这个身份不明的异常。然后,这个异常被一级一级上报,直到当前程序进程的最高领导,最高领导为了顾全大局,果然决定与这个异常同归于尽,也就是让整个应用程序池崩溃。。。

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于IIS应用程序池崩溃的详细内容...

  阅读:57次