2009-06-16 77 views

回答

3

一個線程可以在幾個方面達到停止狀態:

  • 它的主要方法沒有任何錯誤可以退出。
  • 線程上未捕獲的異常可以終止它。
  • 另一個線程可能會調用Thread.Abort(),這將導致該線程拋出ThreadAbortException。

我不知道你是否希望區分所有三種狀態,但如果你真的感興趣的是線程是否成功完成,我會建議使用某種共享數據結構(a同步字典可以工作)線程的主循環在終止時更新。您可以使用ThreadName屬性作爲此共享字典中的鍵。對終止狀態感興趣的其他線程可以從該字典中讀取以確定線程的最終狀態。

在查看了一下MSDN documentation後,您應該能夠使用ThreadState屬性區分外部中止的線程。當一個線程響應Abort()調用時,這應該設置爲ThreadState.Aborted。但是,除非您控制了運行的線程代碼,否則我認爲您不能區分剛剛退出其主方法的線程與以異常終止的線程。但是請記住,如果你控制的線程的代碼是開始的線程,你總是可以替換你自己的方法,在內部調用運行主線程邏輯的代碼並注入異常檢測(如上所述) 。

-1

線程只能通過調用Thread.Abort()來中止,這會導致ThreadAbortException,所以通過異常處理,您應該能夠確定正常退出與中止退出。

+0

據我所知,一個ThreadAbortException只在實際中止的線程中引發,而不是在調用代碼中。因此,我無法在調用代碼中捕獲該異常。 – Pwninstein 2009-06-16 14:57:18

+0

@Pwninstein這是正確的。我假設你可以控制線程代碼。如果您只能訪問調用代碼,那麼我的答案對您沒有任何用處。 – Joseph 2009-06-16 15:01:59

1

你可能想看看BackgroundWorker類。它有一個線程完成時的通用事件處理程序。在那裏,您可以檢查線程是否由於錯誤而完成,因爲它已被取消或成功完成。

+0

有趣的是,你應該提到的是......我實際上實現了BackgroundWorker類的不同風格 - 一個可以取消長時間運行的操作的類。還是)感謝你的建議! – Pwninstein 2009-06-16 15:05:01

1

假設主線程需要等待工作線程成功完成,我通常使用ManualResetEvent。或者,對於多個工作線程,來自Parallels Extensions的新的CountDownEvent,如so

3

是你想監視你的代碼的線程?如果是這樣,你可以把所有東西都包裝在一個類中,當你完成或者使用一個WaitHandle(我將使用一個ManualResetEvent)來發出一個事件來表示完成。 - 完全封裝後臺邏輯。您也可以使用此封裝來捕獲異常,然後引發事件。

事情是這樣的:

 

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 

namespace BackgroundWorker 
{ 
    public class BackgroundWorker 
    { 
     /// 
     /// Raised when the task completes (you could enhance this event to return state in the event args) 
     /// 
     public event EventHandler TaskCompleted; 

     /// 
     /// Raised if an unhandled exception is thrown by the background worker 
     /// 
     public event EventHandler BackgroundError; 

     private ThreadStart BackgroundTask; 
     private readonly ManualResetEvent WaitEvent = new ManualResetEvent(false); 

     /// 
     /// ThreadStart is the delegate that you want to run on your background thread. 
     /// 
     /// 
     public BackgroundWorker(ThreadStart backgroundTask) 
     { 
      this.BackgroundTask = backgroundTask; 
     } 

     private Thread BackgroundThread; 

     /// 
     /// Starts the background task 
     /// 
     public void Start() 
     { 
      this.BackgroundThread = new Thread(this.ThreadTask); 
      this.BackgroundThread.Start(); 

     } 

     private void ThreadTask() 
     { 
      // the task that actually runs on the thread 
      try 
      { 
       this.BackgroundTask(); 
       // completed only fires on successful completion 
       this.OnTaskCompleted(); 
      } 
      catch (Exception e) 
      { 
       this.OnError(e); 
      } 
      finally 
      { 
       // signal thread exit (unblock the wait method) 
       this.WaitEvent.Set(); 
      } 

     } 

     private void OnTaskCompleted() 
     { 
      if (this.TaskCompleted != null) 
       this.TaskCompleted(this, EventArgs.Empty); 
     } 

     private void OnError(Exception e) 
     { 
      if (this.BackgroundError != null) 
       this.BackgroundError(this, new BackgroundWorkerErrorEventArgs(e)); 
     } 

     /// 
     /// Blocks until the task either completes or errrors out 
     /// returns false if the wait timed out. 
     /// 
     /// Timeout in milliseconds, -1 for infinite 
     /// 
     public bool Wait(int timeout) 
     { 
      return this.WaitEvent.WaitOne(timeout); 
     } 

    } 


    public class BackgroundWorkerErrorEventArgs : System.EventArgs 
    { 
     public BackgroundWorkerErrorEventArgs(Exception error) { this.Error = error; } 
     public Exception Error; 
    } 

} 
 

注意:你需要一些代碼,以開始被調用兩次,你可能要添加一個狀態屬性穿過狀態到你的線程。

這裏有一個控制檯應用程序,演示如何使用這個類:

 

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace BackgroundWorker 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Console.WriteLine("Test 1"); 
      BackgroundWorker worker = new BackgroundWorker(BackgroundWork); 
      worker.TaskCompleted += new EventHandler(worker_TaskCompleted); 
      worker.BackgroundError += new EventHandler(worker_BackgroundError); 
      worker.Start(); 
      worker.Wait(-1); 

      Console.WriteLine("Test 2"); 
      Console.WriteLine(); 

      // error case 
      worker = new BackgroundWorker(BackgroundWorkWithError); 
      worker.TaskCompleted += new EventHandler(worker_TaskCompleted); 
      worker.BackgroundError += new EventHandler(worker_BackgroundError); 
      worker.Start(); 
      worker.Wait(-1); 

      Console.ReadLine(); 
     } 

     static void worker_BackgroundError(object sender, BackgroundWorkerErrorEventArgs e) 
     { 
      Console.WriteLine("Exception: " + e.Error.Message); 
     } 

     private static void BackgroundWorkWithError() 
     { 
      throw new Exception("Foo"); 
     } 

     static void worker_TaskCompleted(object sender, EventArgs e) 
     { 
      Console.WriteLine("Completed"); 
     } 

     private static void BackgroundWork() 
     { 
      Console.WriteLine("Hello!"); 
     } 
    } 
} 

 
0

我用CancellationTokenSource問線程正常退出。然後我使用var exitedProperly = _thread.Join(TimeSpan.FromSeconds(10);等待線程退出。

如果exitedProperly==false,我將錯誤推送到日誌中。

我主要使用這種模式,當我在Dispose()函數,我試圖清理我創建的任何線程。

相關問題