2009-02-17 60 views
3

我有一個對象,一個時間軸,封裝線程。活動可以安排在時間表上;該線程將等待執行任何事件,執行它並返回睡眠狀態(無論是(a)到達下一個事件所需的時間,還是(b)如果沒有其他事件,則無限期地)。正確的方法清理C#中的永久線程

使用WaitEventHandle處理睡眠,當事件列表發生更改(因爲可能需要調整睡眠延遲)或應停止線程(因此線程可以正常終止)時觸發WaitEventHandle。

析構函數調用Stop(),我甚至實現了IDisposable和Dispose()也調用Stop()。

但是,當我在窗體應用程序中使用此組件時,我的應用程序將永遠不會正常關閉時關閉窗體。出於某種原因,Stop()永遠不會被調用,因此,在決定等待所有線程完成之前,我的對象的析構函數都不會觸發,也不會調用Dispose()方法。

我想解決方法是在FormClose事件中顯式調用Dispose()我自己,但由於這個類將在庫中,它實際上是一個更深的層(也就是應用程序開發人員將永遠不會看到Timeline類),這對於應用程序開發人員來說似乎非常醜陋和額外的(不必要的)陷阱。在資源發佈成爲問題時,我通常使用的using()子句不適用,因爲這將是一個長期存在的對象。一方面,我可以理解,.NET將在所有線程完成最後一輪垃圾收集之前等待所有線程完成,但在這種情況下會產生非常笨拙的情況。

如何在不向我的圖書館消費者添加要求的情況下正確地清理自己的線索?換句話說,當應用程序正在退出時,如何讓.NET通知我的對象,但在它等待所有線程完成之前呢?


編輯:在迴應的人說,這是可以的客戶端程序要知道的線程:我恭敬地不同意。

正如我在我原來的文章中所說的,線程隱藏在另一個對象(動畫師)中。我爲另一個對象實例化一個Animator,然後告訴它執行動畫,例如「使這盞燈閃爍800毫秒」。作爲Animator對象的使用者,我不在乎Animator如何確保燈光正好閃爍800毫秒。它是否啓動線程?我不在乎。它是否創建一個隱藏的窗口並使用系統計時器(ew)?我不在乎。它是否僱傭侏儒來打開和關閉我的燈光?我不在乎。

而我特別是不希望不得不在意如果我創建了Animator,我必須跟蹤它,並在我的程序退出時調用一個特殊的方法,而不是每個其他對象。它應該是圖書館實施者而不是圖書館消費者的關注點。


編輯:代碼實際上足夠短顯示。我將它列入參考,SANS方法的事件添加到列表:

internal class Timeline : IDisposable { 
    private Thread eventThread; 
    private volatile bool active; 
    private SortedList<DateTime, MethodInvoker> events = new SortedList<DateTime,MethodInvoker>(); 
    private EventWaitHandle wakeup = new EventWaitHandle(false, EventResetMode.AutoReset); 

    internal Timeline() { 
     active = true; 
     eventThread = new Thread(executeEvents); 
     eventThread.Start(); 
    } 

    ~Timeline() { 
     Dispose(); 
    } 

    private DateTime NextEvent { 
     get { 
      lock(events) 
       return events.Keys[0]; 
     } 
    } 

    private void executeEvents() { 
     while (active) { 
      // Process all events that are due 
      while (events.Count > 0 && NextEvent <= DateTime.Now) { 
       lock(events) { 
        events.Values[0](); 
        events.RemoveAt(0); 
       } 
      } 

      // Wait for the next event, or until one is scheduled 
      if (events.Count > 0) 
       wakeup.WaitOne((int)(NextEvent - DateTime.Now).TotalMilliseconds); 
      else 
       wakeup.WaitOne(); 
     } 
    } 

    internal void Stop() { 
     active = false; 
     wakeup.Set(); 
    } 

    public void Dispose() { 
     Stop(); 
    } 
} 

回答

3

我不認爲這是不合理的要求客戶停止()的線程關閉的。有許多方法可以創建線程,這些線程的繼續執行不會阻止應用程序退出(儘管我沒有將頭頂上的細節)。但期望啓動並終止工作者線程對客戶來說不是太大的負擔。

+0

+1 - 在您明確設置了自己的線程對象的時候,您(以及任何調用您的庫)負責告訴它停止,而不是.NET – 2009-02-17 22:38:19

1

沒有客戶端的合作,沒有辦法讓.NET通知你的線程。如果你設計你的庫有一個長時間運行的後臺線程,那麼客戶端應用程序必須被設計爲了解它。

4

也許將T hread.IsBackground屬性設置爲true?

eventThread = new Thread(executeEvents); 
eventThread.IsBackground = true; 
eventThread.Start(); 

另一個選擇是使用Interrupt方法來喚醒它。只要確保你在中斷的線程中捕獲到ThreadInterruptedException,並在發生時關閉。

active = false; 
eventThread.Interrupt(); 
try { eventThread.Join(); } // Wait for graceful shutdown 
catch (Exception) { } 

不太清楚你的那個EventWaitHandle是如何工作的,但...當我做類似的東西一次,我只是用普通Thread.Sleep =)

+0

設置IsBackground爲真正適合我的目的。中斷方法不起作用,當然我可以關閉我的線程,但是我不知道何時觸發關閉。 – rix0rrr 2009-02-19 12:31:14

+0

我不知道這是不是很好的編程習慣,但你可以在終結者中做到這一點?反正,如果IsBackground在你的情況下工作不是問題=) – Svish 2009-02-19 12:33:54

1

Application::ApplicationExit是一個靜態事件,是可以接受聽爲它做你的特別清理工作?

實現IDisposable應該足以說明你的客戶應該在「使用」塊中使用你的類。

0

執行IDisposable properly,包括實現調用Dispose(true)的終結器。然後您可以對Animator對象執行任何清理操作,包括在必要時停止線程。