知识大全 .Net 下跟踪线程挂起和程序死循环

Posted

篇首语:好汉凭志强,好马凭胆壮。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 .Net 下跟踪线程挂起和程序死循环相关的知识,希望对你有一定的参考价值。

.Net 下跟踪线程挂起和程序死循环  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

  下的程序调试相对C/C++要简单很多 少了那些令人头疼的指针越界的问题 不过当你的程序遇到如下问题时 依然非常棘手

  进程异常终止 解决方案见 Net 下未捕获异常的处理

  内存泄漏或者内存申请后程序始终没有释放 解决方案见 用 NET Memory Profiler 跟 应用内存使用情况 基本应用篇 如果通过自己编写的程序监控 我将在以后的文章中阐述

  线程因未知原因挂起 比如死锁

  程序死循环

  本文将阐述如果编写程序对后两者故障实时跟踪并报告

  首先我们需要一个单独的监控线程来监控需要监控的线程

  我做了一个监控类 ThreadMonitor 在开始监控之前 我们将监控线程的优先级设置为最高

          public ThreadMonitor()                    _MonitorThread = new Thread(new ThreadStart(MonitorTask));            _MonitorThread Priority = ThreadPriority Highest;            _MonitorThread IsBackground = true;        

  接下来我们为这个线程提供几个公共方法

  方法让调用者启动监控

  方法用于将需要监控的线程注册到监控列表中

  方法后面说明

  /**////         /// Start monitor        ///         public void Start()                    _MonitorThread Start();                /**////         /// Monitor register        ///         /// Monitor parameter        public void Register(MonitorParameter monitorPara)                    Debug Assert(monitorPara != null);            Debug Assert(monitorPara Thread != null);            if (GetTCB(monitorPara Thread) != null)                            throw new System ArgumentException( Register repeatedly! );                        lock (_RegisterLock)                            _TCBTable Add(monitorPara Thread ManagedThreadId  new TCB(monitorPara));                            public void Heartbeat(Thread t)                    TCB tcb = GetTCB(t);            if (tcb == null)                            throw new System ArgumentException( This thread was not registered! );                        tcb LastHeartbeat = DateTime Now;            tcb HitTimes =  ;            tcb Status &= ~ThreadStatus Hang;        

  下面让我来说说如何监控某个线程挂起

  监控线程提供了一个心跳调用 Heartbeat 被监控的线程必须设置一个定时器定时向监控线程发送心跳 如果监控线程在一定时间内无法收到这个心跳消息 则认为被监控线程非正常挂起了 这个时间又MonitorParameter参数的HangTimeout指定

  光监控到线程挂起还不够 我们必须要报告线程当前挂起的位置才有实际意义 那么如何获得线程当前的调用位置呢? Net framework 为我们提供了获取线程当前堆栈调用回溯的方法 见下面代码

  private string GetThreadStackTrace(Thread t)                    bool needFileInfo = NeedFileInfo;            t Suspend();            StackTrace stack = new StackTrace(t  needFileInfo);            t Resume();            return stack ToString();        

  这里需要说明的是StackTrace(t needFileInfo) 必须在线程t Suspend后 才能调用 否则会发生异常 但Thread Suspend 调用是比较危险的 因为调用者无法知道线程t挂起前的运行状况 可能线程t目前正在等待某个资源 这时强制挂起 非常容易造成程序死锁 不过值得庆幸的是StackTrace(t needFileInfo)的调用不会和其他线程尤其是调用线程产生资源冲突 但我们必须在这一句执行结束后迅速调用 t Resume 结束线程t的挂起状态

  谈完了对线程非正常挂起的监控 再谈谈对程序死循环的监控

  在决定采用我现在的这个方案之前 我曾经想通过 GetThreadTimes 这个API 函数得到被监控线程的实际CPU运行时间 通过这个时间来计算其CPU占有率 但很遗憾 我的尝试失败了 通过非当前线程下调用 GetThreadTimes 无法得到对应线程的CPU时间 (好像非托管线程可以 但 Net的托管线程我试了 确实不行 但原因我还没弄明白)另外GetThreadTimes 统计不够准确 见 对老赵写的简单性能计数器的修改续 关于

  所以没有办法 我采用了一个不是很理想的方案

  定时统计当前进程的TotalProcessorTime 来计算当前线程的CPU占有率 如果这个CPU占有率在一段时间内大于 / (CPU 数)* % 则认为当前进程出现了死循环 这个测试时间由  MonitorParameter参数的DeadCycleTimeout 属性指定

  这就出现了一个问题 我们只知道程序死循环了 但不知道具体是那个线程死循环 那么如何找到真正死循环的线程呢?

  我采用的方法是每秒钟检测一次线程当前状态 如果当前状态为运行状态则表示命中一次 在确认出现死循环后我们在来检查在一个检查周期内的命中次数 如果这个命中次数足够高 则认为是该线程死循环了 不过这样还是有问题 主线程在等待windows 消息时 或者控制台程序线程在等待控制台输入时 该线程的状态居然始终是 Runing 其实是阻塞了 但我没有找到一个很好的方法来得到线程当前处于阻塞状态 怎么办?我想了个笨办法 就是在上面两个条件都符合的情况下再看看在此期间有没有心跳 如果没有心跳 说明死循环了 但如果有心跳也不一定就没有死循环 遇到这种情况 就将可疑的都全部报告了 靠人来判断吧

  我写了一个示例代码 代码中有一个Winform 主线程 和 一个计数器线程 计数器线程每秒记一次数 并更新界面 监控线程检查到非正常挂起或者死循环 将在当前目录下写一个Report log 输出监控报告

  点击Hang后主线程休眠 秒 计数器线程由于要更新界面 也同样会被挂起

  监控线程检查到两个线程挂起后报告如下

  ThreadMonitorEventThread Name:Main threadThread Status:HangThread Stack:   at System Threading Thread SleepInternal(Int millisecondsTimeout)   at System Threading Thread Sleep(Int millisecondsTimeout)   at DotNetDebug Form buttonHang_Click(Object sender EventArgs e)   at System Windows Forms Control OnClick(EventArgs e)   at System Windows Forms Button OnClick(EventArgs e)   at System Windows Forms Button OnMouseUp(MouseEventArgs mevent)   at System Windows Forms Control WmMouseUp(Message& m MouseButtons button Int clicks)   at System Windows Forms Control WndProc(Message& m)   at System Windows Forms ButtonBase WndProc(Message& m)   at System Windows Forms Button WndProc(Message& m)   at System Windows Forms Control ControlNativeWindow OnMessage(Message& m)   at System Windows Forms Control ControlNativeWindow WndProc(Message& m)   at System Windows Forms NativeWindow DebuggableCallback(IntPtr hWnd Int msg IntPtr wparam IntPtr lparam)   at System Windows Forms UnsafeNativeMethods DispatchMessageW(MSG& msg)   at System Windows Forms Application ComponentManager System Windows Forms UnsafeNativeMethods IMsoComponentManager FPushMessageLoop(Int dwComponentID Int reason Int pvLoopData)   at System Windows Forms Application ThreadContext RunMessageLoopInner(Int reason ApplicationContext context)   at System Windows Forms Application ThreadContext RunMessageLoop(Int reason ApplicationContext context)   at System Windows Forms Application Run(Form mainForm)   at DotNetDebug Program Main()   at System AppDomain _nExecuteAssembly(Assembly assembly String[] args)   at System AppDomain ExecuteAssembly(String assemblyFile Evidence assemblySecurity String[] args)   at Microsoft VisualStudio HostingProcess HostProc RunUsersAssembly()   at System Threading ThreadHelper ThreadStart_Context(Object state)   at System Threading ExecutionContext Run(ExecutionContext executionContext ContextCallback callback Object state)   at System Threading ThreadHelper ThreadStart()

   : : PMThreadMonitorEventThread Name:Counter threadThread Status:HangThread Stack:   at System Threading WaitHandle WaitOneNative(SafeWaitHandle waitHandle UInt millisecondsTimeout Boolean hasThreadAffinity Boolean exitContext)   at System Threading WaitHandle WaitOne(Int timeout Boolean exitContext)   at System Threading WaitHandle WaitOne(Int millisecondsTimeout Boolean exitContext)   at System Windows Forms Control WaitForWaitHandle(WaitHandle waitHandle)   at System Windows Forms Control MarshaledInvoke(Control caller Delegate method Object[] args Boolean synchronous)   at System Windows Forms Control Invoke(Delegate method Object[] args)   at System Windows Forms Control Invoke(Delegate method)   at DotNetDebug Form Counter()   at System Threading ThreadHelper ThreadStart_Context(Object state)   at System Threading ExecutionContext Run(ExecutionContext executionContext ContextCallback callback Object state)   at System Threading ThreadHelper ThreadStart()

  点击DeadCycle 按钮后 让计数器线程死循环 但主线程不死循环

  监控线程检查到计数器线程死循环后报告如下

   : : PMThreadMonitorEventThread Name:Counter threadThread Status:HangThread Stack:   at DotNetDebug Form DoDeadCycle()   at DotNetDebug Form Counter()   at System Threading ThreadHelper ThreadStart_Context(Object state)   at System Threading ExecutionContext Run(ExecutionContext executionContext ContextCallback callback Object state)   at System Threading ThreadHelper ThreadStart()

   : : PMThreadMonitorEventThread Name:Counter threadThread Status:Hang DeadCycleThread Stack:   at DotNetDebug Form DoDeadCycle()   at DotNetDebug Form Counter()   at System Threading ThreadHelper ThreadStart_Context(Object state)   at System Threading ExecutionContext Run(ExecutionContext executionContext ContextCallback callback Object state)   at System Threading ThreadHelper ThreadStart()

  下面是示例代码在

using System;using System Collections Generic;using System ComponentModel;using System Data;using System Drawing;using System Text;using System Windows Forms;using System Threading;using Sys Diagnostics;namespace DotNetDebug    public partial class Form  : Form            Thread _CounterThread;        ThreadMonitor _ThreadMonitor = new ThreadMonitor();        bool _DeadCycle = false;        delegate void CounterDelegate();        private void DoDeadCycle()                    while (_DeadCycle)                                        private void Counter()                    int count =  ;            while (true)                            DoDeadCycle();                labelCounter Invoke(new CounterDelegate(delegate()  labelCounter Text = (count++) ToString(); ));                _ThreadMonitor Heartbeat(Thread CurrentThread);                Thread Sleep( );                            public Form ()                    InitializeComponent();                void OnThreadMonitorEvent(object sender  ThreadMonitor ThreadMonitorEvent args)                    StringBuilder sb = new StringBuilder();            sb AppendLine(DateTime Now ToLongTimeString());            sb AppendLine( ThreadMonitorEvent );            sb AppendLine( Thread Name:  + args Name);            sb AppendLine( Thread Status:  + args Status ToString());            sb AppendLine( Thread Stack:  + args StackTrace);            using (System IO FileStream fs =                new System IO FileStream( report log  System IO FileMode Append                  System IO FileAccess Write))                            using (System IO StreamWriter sw = new System IO StreamWriter(fs))                                    sw WriteLine(sb ToString());                                            private void Form _Load(object sender  EventArgs e)                    _ThreadMonitor ThradMonitorEventHandler +=                new EventHandler<ThreadMonitor ThreadMonitorEvent>(OnThreadMonitorEvent);            _CounterThread = new Thread(new ThreadStart(Counter));            _CounterThread IsBackground = true;            _ThreadMonitor Register(new ThreadMonitor MonitorParameter(                Thread CurrentThread   Main thread                     ThreadMonitor MonitorFlag MonitorHang |                ThreadMonitor MonitorFlag MonitorDeadCycle));            _ThreadMonitor Register(new ThreadMonitor MonitorParameter(                _CounterThread   Counter thread                 ThreadMonitor MonitorFlag MonitorHang |                ThreadMonitor MonitorFlag MonitorDeadCycle));            _CounterThread Start();            timerHeartbeat Interval =  ;            timerHeartbeat Enabled = true;            _ThreadMonitor Start();                private void timerHeartBeat_Tick(object sender  EventArgs e)                    _ThreadMonitor Heartbeat(Thread CurrentThread);                private void ButtonDeadCycle_Click(object sender  EventArgs e)                    _DeadCycle = true;                private void buttonHang_Click(object sender  EventArgs e)                    Thread Sleep( );             cha138/Article/program/net/201311/12301

相关参考

知识大全 多线程基于.NET应用程序迅速响应

多线程基于.NET应用程序迅速响应  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! &nb

解决死锁问题的方法主要有几种?

  防止死锁的方法就是从某一环节上打破其死循环,这就要使其三个条件至少有一个不能满足。可分别采取以下的措施。  (1)进程被挂起后释放其占有的资源,即强制使挂起的进程放弃其占用的资源,等待下一次重新申

解决死锁问题的方法主要有几种?

  防止死锁的方法就是从某一环节上打破其死循环,这就要使其三个条件至少有一个不能满足。可分别采取以下的措施。  (1)进程被挂起后释放其占有的资源,即强制使挂起的进程放弃其占用的资源,等待下一次重新申

当Windows应用程序陷入死循环,结束该程序的最好方法为__

当Windows应用程序陷入死循环,结束该程序的最好方法为_____。A、同时按下CTRL+ALT+DEL键,然后选择“结束任务”结束该程序的运行B、同时按下ALT+DE1键,然后选择“结束任务”结束

知识大全 C#创建多线程应用程序

C#创建多线程应用程序  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  在NET和C#中编写一个多

知识大全 Visual C#中编写多线程程序之起步

VisualC#中编写多线程程序之起步  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  net将关

知识大全 关于.net的debug和trace随谈

  这两个功能主要用于对程序进行调试和跟踪把一些相关的结果可以输出到指定的地方在net的Debug模式下Debug和Trace都进行输出在Release模式下默认只开Trace可以通过设置将Trace

知识大全 Java程序中的多线程

Java程序中的多线程  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  一为什么会排队等待?  下

知识大全 多线程的优点和代价

  尽管面临很多挑战多线程有一些优点使得它一直被使用这些优点是  ●资源利用率更好  ●程序设计在某些情况下更简单  ●程序响应更快  资源利用率更好  想象一下一个应用程序需要从本地文件系统中读取和

知识大全 批处理程序中的“多线程”处理代码

大家都知道批处理中运行的都是一步步单进程执行 但如果进程执行比较慢如PING一个不通的IP地址那就会大大影响批处理程序的执行效率  如下内容将简单举例在WINDOWS下使用批处理做多进程并发