2012-11-20 58 views
2

我在Windows服務中有一個線程(STAThread),它執行大量的工作。當Windows服務重新啓動時,我想優雅地停止此線程。停止線程,ManualResetEvent,volatile布爾值或cancellationToken

我知道有幾個方式

  • 揮發性布爾
  • ManualResetEvent的
  • 的CancellationToken

至於我發現Thread.Abort的是一個沒有去的.. 。

最佳實踐是什麼? 工作是在另一個類中進行的,而不是線程啓動的那個類,所以有必要在構造函數中引入一個cancellationToken參數,或者例如有一個volatile變量。但我無法弄清楚什麼是最聰明的。

更新
只是爲了澄清一點我已經包裹起來什麼我談論的一個非常簡單的例子。如前所述,這是在Windows服務中完成的。現在我正在考慮在循環中檢查的volatile布爾值或一個cancellationToken ... 我不能等待循環完成,如下所述,它可能需要幾分鐘時間,使服務器的系統管理員相信當服務需要重新啓動時,服務出現問題......我可以毫無問題地將所有工作放在循環中,而不會出現任何問題,但是我不能用Thread.Abort來做到這一點,它是「邪惡的」 COM接口被調用,所以需要小的清理。

Class Scheduler{ 
    private Thread apartmentThread; 
    private Worker worker; 

    void Scheduling(){ 
    worker = new Worker(); 
    apartmentThread = new Thread(Run); 
    apartmentThread.SetApartmentState(ApartmentState.STA); 
    apartmentThread.Start();  
    } 

    private void Run() { 
    while (!token.IsCancellationRequested) { 
     Thread.Sleep(pollInterval * MillisecondsToSeconds); 
     if (!token.IsCancellationRequested) { 
     worker.DoWork(); 
     } 
    } 
    } 
} 

Class Worker{ 
    //This will take several minutes.... 
    public void DoWork(){ 
    for(int i = 0; i < 50000; i++){ 
     //Do some work including communication with a COM interface 
     //Communication with COM interface doesn't take long 
    } 
    } 
} 

UPDATE
只是檢查的性能,使用的CancellationToken其中isCancelled狀態代碼「審查」,是比使用一個ManualResetEventSlim一個WaitOne的要快得多。一些快速的比喻,如果在for循環中迭代100.000.000次的cancelToken,則花費我大約。 500毫秒,WaitOne的成本約爲500毫秒。 3秒。因此,在這種情況下,使用cancellationToken會更快。

+2

看看有關重置事件和易變的相關問題http://stackoverflow.com/questions/11953234/autoresetevent-vs-boolean-to-stop-a-thread我個人通常使用ManualResetEvent爲你的情況,因爲它是系統專門爲這類任務提供的東西。 –

回答

3

您還沒有發佈夠你實現,但我會強烈建議CancellationToken如果這對你是可用的。從可維護性的角度來看,使用和理解起來非常簡單。如果您決定擁有多個工作線程,也可以設置協作取消。

如果您發現自己處於此線程可能會阻塞很長時間的情況,最好設置您的體系結構,以免發生這種情況。當你告訴他們停下來的時候,你不應該開始不會很好的線程。如果他們在問他們時不停下來,唯一真正的辦法就是拆除進程並讓操作系統殺死他們。

Eric Lippert在一個有點相關的問題here上發佈了一個很好的答案。

+0

正如上面在我的最新更新中提到的,我發現性能明智,最好使用CancellationToken ... –

4

我傾向於使用一個bool標誌,鎖定對象和一個終止()方法,如:

object locker = new object(); 
bool do_term = false; 

Thread thread = new Thread(ThreadStart(ThreadProc)); 
thread.Start(); 

void ThreadProc() 
{ 
    while (true) { 
     lock (locker) { 
      if (do_term) break; 
     } 

     ... do work... 
    } 
} 

void Terminate() 
{ 
    lock (locker) { 
     do_term = true; 
    } 
} 

旁白從終止()中的所有其它字段和方法是私有的「工人」類。

+1

我建議將'bool'標記爲'volatile'。 –

+0

這是真的,它會否定鎖定的需要。 – Lloyd

+0

我只是想知道你爲什麼使用鎖定語句?是因爲你的示例是常見的方式來停止許多線程在同一代碼上運行的位置? – witoong623

2

有些情況下,你會發現你的線程兩種情況:

  • 處理。
  • 阻塞。

在您的線程正在處理某些內容的情況下,您必須等待線程完成處理才能安全退出。如果它是工作循環的一部分,那麼可以使用布爾標誌來終止循環。

在你的線程被阻塞的情況下,你需要喚醒線程並重新處理它。一個線程可能會阻塞在一個ManualResetEvent,一個數據庫調用,一個套接字調用或其他任何你可能阻塞的東西。爲了喚醒它,你必須調用Thread.Interrupt()方法,這將引發一個ThreadInterruptedException

它可能是這個樣子:

private object sync = new object(): 
private bool running = false; 

private void Run() 
{ 
    running = true; 
    while(true) 
    { 
     try 
     { 
      lock(sync) 
      { 
       if(!running) 
       { 
        break; 
       } 
      } 

      BlockingFunction(); 
     } 
     catch(ThreadInterruptedException) 
     { 
      break; 
     } 
    } 
} 

public void Stop() 
{ 
    lock(sync) 
    { 
     running = false; 
    } 
} 

這裏是你如何使用它:

MyRunner r = new MyRunner(); 
Thread t = new Thread(()=> 
{ 
    r.Run(); 
}); 

t.IsBackground = true; 
t.Start(); 

// To stop the thread 
r.Stop(); 

// Interrupt the thread if it's in a blocking state 
t.Interrupt(); 

// Wait for the thread to exit 
t.Join(); 
3

使用WaitHandle,最好是ManualResetEvent。你最好的選擇是讓你的循環完成任何事情。這是實現您的目標的最安全的方式。

ManualResetEvent _stopSignal = new ManualResetEvent(false); // Your "stopper" 
ManualResetEvent _exitedSignal = new ManualResetEvent(false); 

void DoProcessing() { 
    try { 
     while (!_stopSignal.WaitOne(0)) { 
      DoSomething(); 
     } 
    } 
    finally { 
     _exitedSignal.Set(); 
    } 
} 

void DoSomething() { 
    //Some work goes here 
} 

public void Terminate() { 
    _stopSignal.Set(); 
    _exitedSignal.WaitOne(); 
} 

然後使用它:

Thread thread = new Thread(() => { thing.DoProcessing(); }); 
thread.Start(); 

//Some time later... 
thing.Terminate(); 

如果你在你的「DoSomething的」實施特別長的運行過程中,您可能希望異步調用,併爲它提供狀態信息。然而,這可能會變得非常複雜 - 只要等到您的過程完成,然後退出,如果可以的話就更好了。

+0

這將是一個很好的方法來做到這一點,但是不會是我的線程執行DoProcessing是一個STAThread? –

+0

這不應該是一個問題。 (至少不是我跑的時候) –