C#程序死机故障原因分析

序言:

最近在跟踪一些C#程序上死机的Bug,市场报上来也只有一些简单信息,错误信息比较少,查了很MSDN很多资料,对一些死机的情况做了一些分析,特此以本篇文章作总结。

微软官网统有一篇文章讲如何防止Windows程序死机的,分为三个视点(Perspective)说明了死机的概念,以及如何防止死机。参考下面的Link。

http://msdn.microsoft.com/en-us/library/windows/desktop/dd744765(v=vs.85).aspx

该文章主要谈了一些大的方向,很有道理,但没有涉及到一些具体问题。

对于死机,对应有两个英文单词就是Crash/Hang。

Crash,程序状态不明,很多时候是内存错误导致的,尤其C/C++程序中比较多见。

Hang,程序状态应该是明确的,但是该状态下,没有能力处理外部输入。主要是程序设计时没有考虑该状态的转换导致的。

 导致Crash,一般性来说可能是未处理异常,例如内存错误,除零等异常。而导致Hang的,一般来说都是死锁引起的。

一,未处理异常

C#以及Java因为程序为托管代码,内存问题相对比较少(不能够说绝对没有),这里只谈未处理异常的情况。

从.Net框架2.0版本开始,Runtime环境针对为处理异常是有监控的,如果一个程序有未处理异常发生,那么会导致程序终止。

从.Net框架4.0版本开始,程序有未出处理异常,系统可以向用户弹出提示框,提示关闭程序。

所以如果程序状态异常,并且迅速终止,那么可以判断是未处理异常。

C#中提供了“未处理异常”的处理Handle句柄(UI线程异常,以及工作线程异常两种Handle),程序代码中可以定义该Handle对应的处理函数,处理程序中发生未预料的异常。C#产品级代码一定会提供比较完善的异常处理策略,所以一定会实现该Handle处理。

没有考虑到的同学,参考下面Link:

http://msdn.microsoft.com/zh-cn/library/system.windows.application.unhandledexception(v=vs.95).aspx

除此之外,还有哪些未处理异常会导致程序Crash呢?

1,Native代码的异常;

C#开发程序,一般性都会有部分呢Native代码编写的模块做底层处理,C#实现UI以及简单的网络传输等部分。

Native模块处理不当情况下,可能会有意料之外的异常发生,此时会发生Crash,可能的现象就是程序一闪就挂掉,程序上一些错误Log可能都来不及输出。

这里就需要排查Native模块,来锁定问题。如果在发生问题时,能够获得Dump文件,可以通过Dump调用栈来分析Manage代码以及Unmanaged代码的调用栈,可以定位问题所在。

 

2,没有恰当的处理好未处理异常;

首先,普及一个关于异常处理的常识:

一般来说,C/C++程序,如果在异常处理路径上发生新的异常的话,此时程序会进入Crash状态。

所以异常处理程序必须是Exception Free的。理论上将全部代码都是ExceptionFree才是最OK。

但是C#程序,在异常处理路径上发生新异常的话,该异常仍然会被最后的“未处理异常”的处理Handle检测并处理,中途不会Crash。

但是在该Hanle处理时,发生未处理异常的话,那肯定会进入Crash状态。

所以,如果定义了未处理异常的Handle,但是程序仍然没反馈,而直接Terminate掉,那么需要排查其未处理异常的处理函数是否被恰当的实现了,

中途是否有发生新的异常的可能性。

 

二,死锁

普通工作线程的死锁,可以通过Debug环境再现,或者问题发生时生成Dump文件,分析调用栈,能够分析处理。

那么除了工作线程的死锁,还有另外一种概念就是UI线程的死锁。

UI线程的职责就是处理Windows窗体消息,以及用户输入事件。

这里定义UI线程的死锁,为丧失了DumpMessage的能力。

就是UI线程已经停止处理Windows消息了,或者没有能力处理Windows消息,导致与之绑定的窗体均不能够响应用户的动作,我们说hang住了。

这个可能不是由于“锁”的问题产生的,但有类似死锁一样的现象。

 

1,多UI处理线程之间消息处理不当。

某些架构设计,会在在主程序有一个UI线程之外,另外设计UI线程处理部分窗口内容的处理,例如刷新视频等动作。

那么代码中就会涉及到由一个UI线程的消息,通知另外一个UI线程,去实现某些动作。UI线程之间的调用,推荐是异步的(BeginInvoke而不是Invoke)。

如果在父子窗口之间再认为设计一些同步调用,可能会导致两个UI线程之间的死锁。特别是在处理例如窗体状态Size变化,焦点迁移处理等。

 

2,在工作线程上创建了UI控件,导致UI界面不能够反映。

这个属于神秘事件。因为一般来说,不会有意在工作线程中创建UI控件。那为什么在工作线程上创建UI控件的事情会发生呢?

这个与.Net框架,会推迟Form/Control的窗口Handle创建有关。例如:

Form f = new Form();

该时点,创建了Form实例,但是未必创建Handle句柄。创建Handle句柄的开销要比创建一个Form实例大很多。所以.Net框架下,使用了Lazy模式,在使用到该窗体的地方再创建其窗体句柄,例如在Show的时候。那么在窗体显示之前,有针对该窗体的Callback调用的话,例如:

void CallbackHandler(Object senner , EventArg e)

{

   

     if(this.InvokeRequired)

    {

          InvokeMethod delegate = new .....

    } else

   {

          //Handle句柄没有创建的话,InvokeRequired为False,实际该函数可能运行在工作线程上。

           //Do something here

          //创建控件,或者其他窗口。那么控件或者窗口的上下文为该工作线程,而该工作线程无法处理消息。

   }

}

此时,如果广播系统事件到该应用程序,而该程序有些控件或者窗体是在工作线程上创建的,那么回直接导致程序Hang住。因为工作线程没有处理UI事件的能力,导致整个系统都在等待该消息的处理完成。

 

 

 

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值