在一個Service程式中, 通常都會有多個Worker線程,它們可能單獨運行, 也可能在一個ThreadPool中運行。為了不至於使Worker線程的未處理異常導致主程式的崩潰,我們需要對所有的背景工作執行緒以一種一致的方式處理異常,例如通知主線程,然後根據不同的異常做不同的處理,最後優雅地停止該有問題的線程。 例如以下程式:
static void Main(string[] args){ Thread thread1 = new Thread((ThreadStart)Worker_1); thread1.Start(); Thread thread2 = new Thread((ThreadStart)Worker_2); thread2.Start(); thread1.Join(); thread2.Join();}static void Worker_1(){ try { // Do something here. } catch (Exception e) { // TODO, handler exception, // Notify the main thread and stop this thread gracefully. }}static void Worker_2(){ try { // Do something here. } catch (Exception e) { // TODO, handler exception, // Notify the main thread and stop this thread gracefully. }}
在該程式中,我們有 Worker_1 和 Worker_2兩個背景工作執行緒,它們有相同的異常處理過程。但是問題是,當任務的種類多了起來,如Worker_3, Worker_4, 所有的這樣的線程函數都要做相同的異常處理,就導致了不必要的重複,並且很容易遺忘。怎樣去除這種重複呢?首先想到的是一個方案是,提供一個輔助函數,它接受一個Action作為參數:
static void SafeThread(Action action){ try { action(); } catch (Exception e) { // TODO, handler exception, // Notify the main thread and stop this thread gracefully. }}
然後Worker_1 可以這麼寫:
static void Worker_1(){ SafeThread(delegate { // Do something here. });}
這樣是能簡化一些。但這種做法會使原來的Worker方法有一個奇怪的封裝,而且依然要求我們對每一個Worker做同樣的處理。既然Thread的建構函式接受一個 ThreadStart的參數,我們能不能把一個原始的直接的Worker 方法(也是 ThreadStart類型)轉換為一個可以處理異常的 ThreadStart 類型呢? 是可以的。首先我們定義這個轉換函式如下:
static ThreadStart SafeThread(ThreadStart threadStart){ return () => { try { threadStart(); } catch (Exception e) { // TODO, handler exception, // Notify the main thread and stop this thread gracefully. } };}
那麼我們的Worker線程會很直接:
static void Worker_1(){ Console.WriteLine("Worker 1"); // Do something here.}static void Worker_2(){ Console.WriteLine("Worker 2"); // Do something here.}
而主程式則需要稍加改動,但也非常簡單:
static void Main(string[] args){ Thread thread1 = new Thread(SafeThread(Worker_1)); thread1.Start(); Thread thread2 = new Thread(SafeThread(Worker_2)); thread2.Start(); thread1.Join(); thread2.Join();}
這對線程函數的編寫者來說, 減輕了很多負擔, 也不至於會遺漏掉某個線程沒被處理。做一次簡單的搜尋就可以解決問題。
對於接受一個參數的線程(ParameterizedThreadStart)和線程池線程 (WaitCallback),我們又該如何處理呢?