2016-05-31 18 views
1

是否有一個事件會告訴我當前正在運行的線程是否在中途中止,而不是因爲用戶調用了Abort而是因爲程序正在終止?有沒有辦法知道一個線程是否因程序終止而中止?

我需要爲每個線程關閉線程Id的信息。

我希望這將是AppDomainUnload事件,但the documentation說不知道。

This page on the MSDN說:

當運行時停止後臺線程,因爲進程正在關閉,也不例外在線程中拋出。但是,當線程由於AppDomain.Unload方法卸載應用程序域而停止時,ThreadAbortException會在前臺和後臺線程中拋出。

爲了測試這個事實,我編寫了這個小程序,它生成兩個長時間運行的線程,其中一個是前臺線程,另一個是後臺線程,並立即繼續退出當前進程,從而嘗試卸載當前的應用程序域。

這應該不僅調用DomainUnload處理程序,而且還嘗試中止兩個正在運行的線程並向它們發送一個ThreadAbortException

但沒有發生。

輸出窗口顯示以下輸出:

[#10] [Foreground]Starting to do stuff. 
The thread 0x183c has exited with code 0 (0x0). 
[#9] [Worker]Starting to do stuff. 
[#10] [Foreground]Slept for 5 seconds. Now finishing up the doing of stuff. 
The thread 0x2954 has exited with code 0 (0x0). 
The program '[11224] KnowAboutDyingThread.vshost.exe' has exited with code 0 (0x0). 

這是我的代碼:

using System; 
using System.Diagnostics; 
using System.Threading; 

namespace KnowAboutDyingThread 
{ 
    // Source: https://msdn.microsoft.com/en-us/library/h339syd0%28v=vs.110%29.aspx 
    // When the runtime stops a background thread because the process is shutting down, 
    // no exception is thrown in the thread. However, when threads are stopped because the 
    // AppDomain.Unload method unloads the application domain, a ThreadAbortException is 
    // thrown in both foreground and background threads. 

    // I am trying that out 
    // I asked a question here about it: http://stackoverflow.com/questions/37552668/is-there-a-way-to-know-if-a-thread-is-being-aborted-because-the-program-is-termi 
    // Permalink: http://stackoverflow.com/q/37552668/303685 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      AppDomain.CurrentDomain.DomainUnload += CurrentDomain_DomainUnload; 
      ThreadPool.QueueUserWorkItem(Do); 
      var t = new Thread(Do); 
      t.Name = "Foreground"; 
      t.Start(); 

      Process.GetCurrentProcess().Close(); 
     } 

     private static void CurrentDomain_DomainUnload(object sender, EventArgs e) 
     { 
      Debug.Print("Unloading current appdomain..."); 
     } 

     static void Do(object state) 
     { 
      try 
      { 
       Debug.Print(string.Format($"[#{Thread.CurrentThread.ManagedThreadId}] [{Thread.CurrentThread.Name ?? "Worker"}]Starting to do stuff.")); 
       Thread.Sleep(5000); 
       Debug.Print(string.Format($"[#{Thread.CurrentThread.ManagedThreadId}] [{Thread.CurrentThread.Name ?? "Worker"}]Slept for 5 seconds. Now finishing up the doing of stuff.")); 
      } 
      catch(ThreadAbortException abort) 
      { 
       Debug.Print(string.Format($"[#{Thread.CurrentThread.ManagedThreadId}] [{Thread.CurrentThread.Name ?? "Worker"}]Do got ThreadAbortException: {abort.GetType().Name}: {abort.Message}")); 
      } 
      catch(Exception ex) 
      { 
       Debug.Print(string.Format($"[#{Thread.CurrentThread.ManagedThreadId}] [{Thread.CurrentThread.Name ?? "Worker"}]Do had an exception: {ex.GetType().Name}: {ex.Message}")); 
      } 
     } 
    } 
} 





    [1]: https://msdn.microsoft.com/en-us/library/system.appdomain.domainunload%28v=vs.110%29.aspx 
+0

不要'Thread.Abort'。無論你使用什麼,你都會採取錯誤的做法。 'Thread.Abort'是邪惡的。你應該問「如何在不調用'Thread.Abort'的情況下終止我的程序」。再一次,['Thread.Abort'是邪惡的](http://stackoverflow.com/questions/3632149/question-about-terminating-a-thread-cleanly-in-net) – spender

+0

我很清楚這個問題使用'Thread.Abort'和'Thread.Interrupt',我的問題與使用這些方法的副作用無關。 –

+1

我想你需要做'Process.GetCurrentProcess()。Kill();'來獲得你想要的行爲。執行'Close()'會觸發正常的關閉,但forground線程上的'Do'沒有辦法監視關閉消息,因此不會觸發進程。即使如此,'DomainUnload'仍然可能因爲強制終止而無法啓動,但至少您可能無法獲得「正在完成...」部分。 –

回答

0

背景線程不應該被正常終止。

您將無法捕獲任何異步異常。這也適用於前臺線程。

在前臺線程中,您將能夠優雅地進入finally塊,但不能捕獲塊。

你能做的就是可能使用最終確定對象的最簡單的事情:

using System; 
using System.Diagnostics; 
using System.Threading; 

namespace ReliableStop 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Debug.WriteLine("-- Started Main. Thread: {0}", Thread.CurrentThread.ManagedThreadId); 

      var everStarted = new ManualResetEvent(false); 

      var t = new Thread(o => 
      { 
       Debug.WriteLine("-- Thread entered here."); 
       everStarted.Set(); 

       using (new PostMortemThreadDump()) 
       { 
        try 
        { 
         Thread.Sleep(100); 
        } 
        catch 
        { 
         Debug.WriteLine("-- Attempt to catch everything."); 
        } 
        finally 
        { 
         Debug.WriteLine("-- Attempt to process finally."); 
        }      
       } 
      }); 
      t.IsBackground = true; 
      t.Start(); 

      // Check that the thread has started. 
      everStarted.WaitOne(); 

      // ... Good bye, no need to kill, I will die on my own. 
     } 
    } 

    class PostMortemThreadDump : IDisposable 
    { 
     int threadId { get; } 
     public PostMortemThreadDump() 
     { 
      threadId = Thread.CurrentThread.ManagedThreadId; 
     } 

     ~PostMortemThreadDump() 
     { 
      Dispose(false); 
     } 

     void Dispose(bool disposing) 
     { 
      if (disposing) 
      { 
       Debug.WriteLine("-- PostMortemThreadDump. Finished normally."); 
      } 
      else 
      { 
       Debug.WriteLine("-- PostMortemThreadDump. Thread: {0}", threadId); 
      } 
     } 

     public void Dispose() 
     { 
      Dispose(true); 
      GC.SuppressFinalize(this); 
     } 
    } 
} 

輸出:

-- Started Main. Thread: 9 
-- Thread entered here. 
The thread 0x6fc8 has exited with code 0 (0x0). 
The thread 0x12b4 has exited with code 0 (0x0). 
-- PostMortemThreadDump. Thread: 10 
The program '[25260] ReliableStop.vshost.exe' has exited with code 0 (0x0). 

現在,如果你改變使用一個前臺線程,你也得到了`finally塊執行:

-- Started Main. Thread: 9 
-- Thread entered here. 
The thread 0x4d54 has exited with code 0 (0x0). 
The thread 0x20c8 has exited with code 0 (0x0). 
-- Attempt to process finally. 
-- PostMortemThreadDump. Finished normally. 
The thread 0x6928 has exited with code 0 (0x0). 
The program '[26328] ReliableStop.vshost.exe' has exited with code 0 (0x0). 

正如你所看到的,終結器運行在兩種情況下,地面線程,你得到finally塊執行。

調用Process.GetCurrentProcess().Close()不會影響此場景。

相關問題