2011-10-20 73 views
5

在我正在開發的一個應用程序中,我有一個簡單的坐在那裏並顯示日誌數據的主窗體,以及一個自主完成循環工作的工作線程。優雅地關閉了一個線程

MyWorker worker = new MyWorker(); 
MainForm mainForm = new MainForm(); 

// Subscribe form to log event so log data gets displayed 
worker.Log += mainForm.Log; 

// Start the worker thread's MainLoop 
new Thread(new ThreadStart(worker.MainLoop)).Start(); 

// Show the form (blocking) 
Application.Run(mainForm); 

// If we end up here, the form has been closed and the worker has to stop running    
worker.Running = false; 

正如您所看到的,只要窗體關閉,工作線程應該停止。這名工人是這樣的:

public class MyWorker 
{ 
    public String Running { get; set; } 

    public MyWorker() 
    { 
     Running = true; 
    } 

    public void MainLoop() 
    { 

     while (Running) 
     { 

      DoExtensiveWork1(); 
      if (!Running) return; 

      DoExtensiveWork2(); 
      if (!Running) return; 

      DoExtensiveWork3(); 
      if (!Running) return; 

      DoExtensiveWork4(); 
      if (!Running) return; 

      DoExtensiveWork5();   
      if (!Running) return; 

      // We have to wait fifteen minutes (900 seconds) 
      // before another "run" can be processed 
      for (int i = 0; i < 900; i++) 
      { 
       Thread.Sleep(1000); 
       if (!Running) return; 
      } 
     } 
    } 
} 

正如你所看到的,我想線程能夠連續工作的操作之間切換時停止,而不是在一個操作中。當一個操作(DoExtensiveWorkN)完成時,其狀態和結果將保留在磁盤或數據庫中,因此在操作正在進行時退出(例如,通過Thread.Abort)不是選項。

但是,我發現這段代碼我剛剛寫了一些令人厭惡的代碼,特別是睡眠時間爲900秒的「等待循環」,以防止線程在檢測到Running已被設置爲false

我寧願能夠拋出某種事件來完成一項工作後立即停止主循環。

任何人都可以指出我正確的方向如何做到這一點,或者如果因爲我完全誤解了線程而需要全部重寫,請告訴我某處解釋這些原則的地方?

回答

13

您可以清理各個任務的運行和15分鐘等待循環。

我建議也許使用這樣的事情:

public class MyWorker 
{ 
    private readonly ManualResetEvent _stopEvent = new ManualResetEvent(false); 
    private readonly Action[] _workUnits; 

    private bool Running 
    { 
     get { return !_stopEvent.WaitOne(0); } 
    } 

    public MyWorker() 
    { 
     _workUnits = new Action[] 
     { 
      DoExtensiveWork1, 
      DoExtensiveWork2, 
      DoExtensiveWork3, 
      DoExtensiveWork4, 
      DoExtensiveWork5 
     }; 
    } 

    public void Stop() 
    { 
     _stopEvent.Set(); 
    } 

    public void MainLoop() 
    { 

     while (Running) 
     { 
      foreach (var workUnit in _workUnits) 
      { 
       workUnit(); 
       if (!Running) return; 
      }   

      // We have to wait fifteen minutes (900 seconds) 
      // before another "run" can be processed 
      if (_stopEvent.WaitOne(900000)) return; 
     } 
    } 
} 

然後在下一個合適的點停止進程:

Worker.Stop(); 
0

我建議使用System.Timers.Timer

你可以用跑步的東西做你的工作,而不是使用睡眠,你可以設置定時器在15分鐘內再次關閉。

如果您想盡早停止它,請調用某種終止方法(類似於設置您的Running = true變量)來停止計時器。

應該指出的是,每當計時器事件觸發時,它將啓動一個新的線程,所以你不需要擔心殺死後臺線程。您的線程完成其運行處理,將計時器設置爲在15分鐘內運行,然後線程自然結束。如果您在等待期間中止,那麼您只需擺脫計時器,無需進行更多清理。如果在運行過​​程中中止,則讓運行結束,並在結束時檢查一個標誌,並且不會再次啓動計時器,然後線程結束。

對於計時器,您需要將計時器設置爲在過程結束時手動啓動。另一種方法是讓計時器每15分鐘計時一次,但這意味着如果您的處理花費了10分鐘,那麼它只能在下一次運行之前花5分鐘。如果超過15分鐘,您可能會遇到麻煩。還手動重新啓動計時器可確保在另一個正在運行時處理不應重新啓動。

+0

定時器不會解決我遇到的主要問題:我不希望工作在表單關閉時繼續進行,並且我不想添加大量代碼來檢查該問題。 – CodeCaster

+0

@CodeCaster:啊,我誤解了「在操作正在進行時退出(例如,通過Thread.Abort)不是一個選項」來表示整個線程。我的想法然後會使用一系列的行動,但銥星擊敗了我。儘管如此,我仍然更喜歡15分鐘的計時器。 :) – Chris

+0

我可以更清楚地描述。 (還是)感謝你的建議。 :-) – CodeCaster