2010-10-15 72 views
14

我正在寫一個GUI應用程序。如何正確關閉創建多個線程的C#應用​​程序?

應用程序在其生命週期中打開多個線程。其中一個線程是處理可能來自其他應用程序的事件,所以它正在等待一段時間(true)循環中的事件,該事件從未終止。

用戶可以在任何時間關閉應用程序。我想關閉主應用程序打開的所有線程。我使用Process.GetCurrentProcess()。Kill();}。目前正在處理這個問題。

這是一個很好的解決方案嗎?如果沒有,爲什麼以及如何處理這個問題的正確方法,如何關閉主應用程序打開的所有線程?

+0

你應該避免'while(true)'的閒置工作。至少要增加一個「Sleep(100)」,讓你的CPU有一些喘息的空間。查看一些免費的書籍,如http://www.albahari.com/threading/或http://www.csharpcourse.com/,瞭解如何在不做空閒工作的情況下保持線程正常運行的細節。 – ja72 2010-10-15 19:03:20

+1

while(true)循環等待事件。由於它有一個阻塞的waitOne(),它不會殘酷地使用我的cpu。 – Alex 2010-10-16 11:03:23

回答

23

如果您創建新線程作爲後臺線程(通過在啓動它們之前設置IsBackground),它們將在主線程(應用程序線程)終止時自動停止。

(從MSDN):

線程可以是一個背景線程或前臺線程。後臺線程與前臺線程完全相同,只是後臺線程不會阻止進程終止。一旦屬於某個進程的所有前臺線程都終止,公共語言運行庫就結束該進程。任何剩餘的後臺線程都會停止並且無法完成。

+1

設置'IsBackground = true'聽起來可能適合您的情況。當我有一個線程在'Console.ReadLine'上持續阻塞並等待輸入時,我不得不使用該屬性。 *但是,在大多數情況下,您應該嘗試設計程序,以便在計算或處理線程完成之前不允許關閉,或者可以在計算或處理代碼中構建取消機制。 – jnylen 2010-10-15 18:13:41

+0

我同意@jnylen,你想有一些機制讓你的線程優雅地退出,而不是在某些操作中可能會關閉。 – CodingGorilla 2010-10-15 18:17:41

+0

@jnylen:是的,如果線程正在做**需要**乾淨的終止(例如寫入文件),則需要通過發信號通知他們停止然後等待來清除終止。如果線程可以被中斷,將它們設置爲後臺線程就足夠了。 – adrianbanks 2010-10-15 18:21:48

2

一旦你已經有線程等待某些事件,只需添加一個事件,當觸發時會指示線程終止。對於這個話題的深入討論see MSDN -

如果您不需要提供其他線程正常關機的一些手段,你可以切換到他們的「後臺線程」模式,以確保自動終止。

1

有很多方法可以解決這個問題,但理想情況下,您希望自己的線程能夠自行正常退出,而不僅僅是殺死進程。

你可以做這樣的事情很簡單:

public class ThreadSignal 
{ 
    public bool Stop { get; set; } 
} 

然後在你的線程循環,這樣做:

public void DoWork(object state) 
{ 
    ThreadSignal signal = (ThreadSignal)state; 
    while(!signal.Stop) 
    { 
     // Do work here  
    } 
} 

然後,當你已經做好準備停止,設置您的ThreadSignal.Stoptrue。這是一個非常簡單的例子,但它給了你一個起點。

0

正如你所說,它是一個GUI應用程序,所以負責消息循環的主線程負責提醒用戶想要退出程序的無限循環(while(true))。我建議將true替換爲另一個boolean,以表示用戶已關閉窗口,如下所示:while(windowIsOpen)並在卸載窗體時將其設置爲false。

1

您應該在循環中用ManualResetEvent(或AutoResetEvent)等待。 然後,只需設置一個成員變量設置爲true,當你關閉:

public class MyForm : Form 
{ 
    private AutoResetEvent _workTrigger = new AutoResetEvent(); 
    private bool _shuttingDown = false; 
    private Thread _thread; 

    public void Form_Initialize() 
    { 
     _thread = new Thread(MyThreadMethod); 
     _thread.Start(); 
    } 

    public static void MyThreadMethod(object State) 
    { 
     while (!_shuttingDown) 
     { 
      //wait for jobs. 
      _workTrigger.WaitOne(); //can add a timeout as parameter. 

      //do some work here 

     } 

    } 


    public void Form_Closing(object source, EventArgs e) 
    { 
     _shuttingDown = true; 
     _workTrigger.Set(); 

     //wait for it to exit. You could use the timeout 
     //parameter and a loop to not block the UI 
     _thread.Join(); 
    } 
} 
0

不要失去你的線程在應用程序中 - 地方keep'em(List<Thread>會做得很好)。然後當時間正確時(關閉時間)通知每個人它應該完成正在做的事情並退出。

然後,.Join()所有這些,然後允許應用程序退出。

永遠不要去'ThreadAbort'領域,它是潛伏在那裏的力量的黑暗面。

0

我一般是如何做到這一點:

  • 創建封裝此行爲的類(例如,在後臺
  • 有自IDisposable的類繼承處理進來的消息時的Dispose()被調用設置命名爲私有變量_disposed
  • 在我班上的構造函數創建我的專用線程。
  • 有一個名爲_workToDo私人的AutoResetEvent。你的後臺線程將等待此事件上只有做好當此事件標誌着一個工作循環。
  • 有一個公共方法將消息發送給後臺工作人員,將工作排隊,然後設置_workToDo以告知後臺線程完成工作。

把所有這些組合起來,你會得到:

public class BackgroundProcessor : IDisposed 
{ 
    private Thread _backgroundThread; 
    private bool _disposed; 
    private AutoResetEvent _workToDo = new AutoResetEvent(false); 
    // where T is a class with the set of parameters for your background work 
    private Queue<T> _workQueue = Queue.Synchronized(new Queue<T>); 

    public BackgroundProcessor() 
    { 
    _backgroundThread = new Thread(DoBackgroundWork); 
    _backgroundThread.Start(); 
    } 

    public void Dispose() 
    { 
    _disposed = true; 

    // Wait 5 seconds for the processing of any previously submitted work to finish. 
    // This gives you a clean exit. May want to check return value for timeout and log 
    // a warning if pending background work was not completed in time. 
    // If you're not sure what you want to do yet, a Debug.Assert is a great place to 
    // start because it will let you know if you do or don't go over time in general 
    // in your debug builds. 
    // Do *not* Join() and wait infinitely. This is a great way to introduce shutdown 
    // hangs into your app where your UI disappears but your process hangs around 
    // invisibly forever. Nasty problem to debug later... 
    Debug.Assert(_backgroundThread.Join(5000)); 
    } 

    // Called by your 'other application' 
    public void GiveMeWorkToDo(T workParameters) 
    { 
    _workQueue.Enqueue(workParameters); 
    _workToDo.Set(); 
    } 

    private void DoBackgroundWork() 
    { 
    while (!_disposed) 
    { 
     // 500 ms timeout to WaitOne allows your Dispose event to be detected if there is 
     // No work being submitted. This is a fancier version of a Thread.Sleep(500) 
     // loop. This is better because you will immediately start work when a new 
     // message is posted instead of waiting for the current Sleep statement to time 
     // out first. 
     _workToDo.WaitOne(500); 

     // It's possible multiple sets of work accumulated or that the previous loop picked up the work and there's none left. This is a thread safe way of handling this. 
     T workParamters = _workQueue.Count > 0 ? workParameters = _workQueue.Dequeue() : null; 
     do 
     { 
     DoSomething(workParameters); 

     workParameters = _workQueue.Count > 0 ? workParameters = _workQueue.Dequeue() : null; 
     } while (workParameters != null) 
    } 
    } 
} 
0

考慮使用BackGroundWorker類。由於它使用線程池(通過BeginInvoke()),你會得到後臺線程。作爲獎勵,您可以獲得方便的進度報告,取消和完成回調(已經編組到UI線程)。

相關問題