<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      eaglet

      本博專注于基于微軟技術的搜索相關技術
        博客園  :: 首頁  :: 新隨筆  :: 聯系 :: 訂閱 訂閱  :: 管理

      .Net 下跟蹤線程掛起和程序死循環

      Posted on 2009-03-12 13:00  eaglet  閱讀(10023)  評論(3)    收藏  舉報

      .Net 下調試跟蹤線程掛起和程序死循環

      作者:Eaglet

           .Net 下的程序調試相對C/C++要簡單很多,少了那些令人頭疼的指針越界的問題。不過當你的程序遇到如下問題時,依然非常棘手:

           1. 進程異常終止。解決方案見 .Net 下未捕獲異常的處理

           2. 內存泄漏或者內存申請后程序始終沒有釋放。解決方案見 用 .NET Memory Profiler 跟蹤.net 應用內存使用情況--基本應用篇 。如果通過自己編寫的程序監控,我將在以后的文章中闡述。

           3. 線程因未知原因掛起,比如死鎖。

           4. 程序死循環。

           本文將闡述如果編寫程序對后兩者故障實時跟蹤并報告。

      •      首先我們需要一個單獨的監控線程來監控需要監控的線程

           我做了一個監控類 ThreadMonitor,在開始監控之前,我們將監控線程的優先級設置為最高。

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

              }
      •      接下來我們為這個線程提供幾個公共方法

      Start  方法讓調用者啟動監控

      Register 方法用于將需要監控的線程注冊到監控列表中

      Heartbeat 方法后面說明

              /// <summary>
              
      /// Start monitor
              
      /// </summary>

              public void Start()
              
      {
                  _MonitorThread.Start();
              }



              
      /// <summary>
              
      /// Monitor register
              
      /// </summary>
              
      /// <param name="monitorPara">Monitor parameter</param>

              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 
      = 0;
                  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 統計不夠準確 見 對老趙寫的簡單性能計數器的修改續- 關于 GetThreadTimes

      所以沒有辦法,我采用了一個不是很理想的方案

      定時統計當前進程的TotalProcessorTime 來計算當前線程的CPU占有率,如果這個CPU占有率在一段時間內大于 100 / (CPU 數)* 90% ,則認為當前進程出現了死循環。這個測試時間由  MonitorParameter參數的DeadCycleTimeout 屬性指定。

      這就出現了一個問題,我們只知道程序死循環了,但不知道具體是那個線程死循環,那么如何找到真正死循環的線程呢?

      我采用的方法是每秒鐘檢測一次線程當前狀態,如果當前狀態為運行狀態則表示命中一次,在確認出現死循環后我們在來檢查在一個檢查周期內的命中次數,如果這個命中次數足夠高,則認為是該線程死循環了。不過這樣還是有問題,主線程在等待windows 消息時 或者控制臺程序線程在等待控制臺輸入時,該線程的狀態居然始終是 Runing ,其實是阻塞了,但我沒有找到一個很好的方法來得到線程當前處于阻塞狀態。怎么辦?我想了個笨辦法,就是在上面兩個條件都符合的情況下再看看在此期間有沒有心跳,如果沒有心跳,說明死循環了。但如果有心跳也不一定就沒有死循環,遇到這種情況,就將可疑的都全部報告了,靠人來判斷吧。

      我寫了一個示例代碼,代碼中有一個Winform 主線程 和 一個計數器線程,計數器線程每秒記一次數,并更新界面。監控線程檢查到非正常掛起或者死循環,將在當前目錄下寫一個Report.log 輸出監控報告。

      點擊Hang后主線程休眠20秒,計數器線程由于要更新界面,也同樣會被掛起。

      監控線程檢查到兩個線程掛起后報告如下:

      2:38:40 PM
      ThreadMonitorEvent
      Thread Name:Main thread
      Thread Status:Hang
      Thread Stack:   at System.Threading.Thread.SleepInternal(Int32 millisecondsTimeout)
         at System.Threading.Thread.Sleep(Int32 millisecondsTimeout)
         at DotNetDebug.Form1.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, Int32 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, Int32 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(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
         at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
         at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 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()


      2:38:40 PM
      ThreadMonitorEvent
      Thread Name:Counter thread
      Thread Status:Hang
      Thread Stack:   at System.Threading.WaitHandle.WaitOneNative(SafeWaitHandle waitHandle, UInt32 millisecondsTimeout, Boolean hasThreadAffinity, Boolean exitContext)
         at System.Threading.WaitHandle.WaitOne(Int64 timeout, Boolean exitContext)
         at System.Threading.WaitHandle.WaitOne(Int32 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.Form1.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 按鈕后,讓計數器線程死循環,但主線程不死循環。

      監控線程檢查到計數器線程死循環后報告如下:

       

      2:37:51 PM
      ThreadMonitorEvent
      Thread Name:Counter thread
      Thread Status:Hang
      Thread Stack:   at DotNetDebug.Form1.DoDeadCycle()
         at DotNetDebug.Form1.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()


      2:37:52 PM
      ThreadMonitorEvent
      Thread Name:Counter thread
      Thread Status:Hang, DeadCycle
      Thread Stack:   at DotNetDebug.Form1.DoDeadCycle()
         at DotNetDebug.Form1.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 Form1 : Form
          
      {
              Thread _CounterThread;
              ThreadMonitor _ThreadMonitor 
      = new ThreadMonitor();
              
      bool _DeadCycle = false;

              
      delegate void CounterDelegate();

              
      private void DoDeadCycle()
              
      {
                  
      while (_DeadCycle)
                  
      {
                  }

              }


              
      private void Counter()
              
      {
                  
      int count = 0;
                  
      while (true)
                  
      {
                      DoDeadCycle();
                      labelCounter.Invoke(
      new CounterDelegate(delegate() { labelCounter.Text = (count++).ToString(); }));
                      _ThreadMonitor.Heartbeat(Thread.CurrentThread);

                      Thread.Sleep(
      1000);
                  }

              }


              
      public Form1()
              
      {
                  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 Form1_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"100005000,
                      ThreadMonitor.MonitorFlag.MonitorHang 
      |
                      ThreadMonitor.MonitorFlag.MonitorDeadCycle));

                  _ThreadMonitor.Register(
      new ThreadMonitor.MonitorParameter(
                      _CounterThread, 
      "Counter thread",
                      ThreadMonitor.MonitorFlag.MonitorHang 
      |
                      ThreadMonitor.MonitorFlag.MonitorDeadCycle));

                  _CounterThread.Start();

                  timerHeartbeat.Interval 
      = 1000;
                  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(
      20000);
              }

          }

      }

       

       

       

       

      主站蜘蛛池模板: 久久久久无码精品国产不卡| 久久精品不卡一区二区| 亚洲欧美一区二区成人片| 亚洲中文字幕精品第三区| 人妻中文字幕精品一页| 亚洲AV永久无码精品秋霞电影影院| 久久久久久久久久久久中文字幕| 麻豆亚洲精品一区二区| 亚洲国产美国产综合一区| 亚洲精品日韩久久精品| 欧美人伦禁忌dvd放荡欲情| 亚洲国产一区二区三区| 天干天干天啪啪夜爽爽99| 久久夜色撩人精品国产av| 久久亚洲精品无码va白人极品| 免费视频欧美无人区码| 久章草在线精品视频免费观看 | 白嫩少妇激情无码| 下面一进一出好爽视频| 成 人免费va视频| 国产成人免费永久在线平台 | 日韩精品久久久肉伦网站| 色狠狠色噜噜AV一区| 免费看欧美日韩一区二区三区 | 亚洲日韩国产中文其他| 亚洲韩国精品无码一区二区三区| 国产精品露脸视频观看| 亚洲成人高清av在线| 全黄h全肉边做边吃奶视频| 精品无码成人片一区二区| 久久99日韩国产精品久久99| 国产精品va无码一区二区| 国产360激情盗摄全集| 亚洲 都市 无码 校园 激情| 99精产国品一二三产品香蕉| 日韩午夜无码精品试看| 亚洲精品动漫一区二区三| 亚洲国产成人av国产自| 苏尼特左旗| 久草热在线视频免费播放| 强奷乱码欧妇女中文字幕熟女|