[英]Closing side thread before closing the main form
我正在使用C#
在Visual Studio .NET 2010中使用Windows窗體創建GUI應用程序。 我有多個線程,例如:
Control.Invoke()
調用主窗體控件中的更改。 我的目標:當用戶在主窗體上按(x)時,我想確保正確關閉窗體。 這意味着我希望我的連接關閉,所有側線程終止等。我正在嘗試使用FormClosing
事件。 在那里我將KillThreads標志設置為true並等待側線程終止。 每個都有一張支票,比如 if(KillThreads)
return;
但是有一個問題。 如果我試圖等待所有線程終止
for(;;)
if(ActiveSideThreads == 0)
break;
closeConnection();
線程N3凍結被Invoke()
調用阻塞; 當然是,因為主gui線程處於無限循環中。 所以表格結束會永遠延遲。
如果我將on-close操作實現為線程過程4,並在FromClosing事件處理程序中創建一個新線程,則表單會在線程4終止之前立即關閉,而Invoke()
會拋出錯誤-t。 4無法及時關閉線程3。 如果我添加Join(), Invoke()
則再次阻止Join(), Invoke()
調用,並且線程3永遠不會終止,因此線程4將永遠等待。
我該怎么辦? 有什么方法可以解決這個問題嗎?
首先決定你是否真的需要使用Invoke
。 我現在已經有一段時間了,我認為Invoke
是從工作線程啟動后在UI線程上發生操作最常用的技術之一。 另一種方法是在工作線程中創建某種類型的消息對象,描述需要在UI線程上執行的操作並將其放入共享隊列。 然后,UI線程將使用System.Windows.Forms.Timer
在某個時間間隔輪詢此隊列,並導致每個操作發生。 這有幾個優點。
Invoke
強加的UI和工作線程之間的緊密耦合。 當然, Invoke
非常有用,並且有很多理由繼續使用這種方法。 如果您決定繼續使用Invoke
請閱讀更多內容。
一個想法是在Form.Closing
事件中設置KillThreads
標志,然后通過設置FormClosingEventArgs.Cancel = true
取消關閉表單。 您可能希望讓用戶知道已請求關閉並且正在進行中。 您可能希望禁用表單上的某些控件,以便無法啟動新操作。 所以基本上我們發信號通知已經請求了關閉,但是推遲關閉表單直到工作線程先關閉。 要做到這一點,你可能想要啟動一個定時器,定期檢查工作線程是否已經結束,如果有,那么你可以調用Form.Close
。
public class YourForm : Form
{
private Thread WorkerThread;
private volatile bool KillThreads = false;
private void YourForm_Closing(object sender, FormClosingEventArgs args)
{
// Do a fast check to see if the worker thread is still running.
if (!WorkerThread.Join(0))
{
args.Cancel = true; // Cancel the shutdown of the form.
KillThreads = true; // Signal worker thread that it should gracefully shutdown.
var timer = new System.Timers.Timer();
timer.AutoReset = false;
timer.SynchronizingObject = this;
timer.Interval = 1000;
timer.Elapsed =
(sender, args) =>
{
// Do a fast check to see if the worker thread is still running.
if (WorkerThread.Join(0))
{
// Reissue the form closing event.
Close();
}
else
{
// Keep restarting the timer until the worker thread ends.
timer.Start();
}
};
timer.Start();
}
}
}
上面的代碼調用Join
,但它指定超時為0,這會導致Join
調用立即返回。 這應該保持UI抽取消息。
我會在你的線程中有一個執行Invoke的頂級異常處理程序,如果它發現KillThreads
為真,它會吞下Invoke
產生的異常。 然后你需要的是主線程將KillThreads設置為true並允許消息循環結束。
我的一般技術,而不是一個簡單的布爾變量,將使用ManualResetEvent
作為其他線程中止的信號。 這樣做的主要原因是,如果我有長時間運行的非UI線程,有時候想睡覺。 通過使用該事件,我可以通過調用Event.WaitOne
替換對Thread.Sleep
的調用,這意味着如果線程必須中止,線程將始終立即喚醒。
在任何一種情況下,我通常不會等待線程發出完成信號。 我相信他們會及時這樣做,並且不會傾向於使用依賴於未運行的線程的后續代碼。 如果你確實有這種情況,也許你可以詳細說明它是什么?
您可以在所有線程上調用Thread.Join
並通過Thread.Abort
如果它們無法在給定的超時內加入。 Abort的調用在線程的上下文中引發了ThreadAbortException,結束了這個線程。
我認為主要觀點(主線程是否等待其他線程結束,或者不是)在調用GUI線程上的操作之前檢查KillThreads是否為false,除非你真的想在之前向用戶顯示一些內容退出,在這種情況下你應該循環你的主線程,檢查所有工作線程的ThreadState或IsAlive屬性,以確定它們是否已經完成然后退出(或者在完成所有工作線程后執行任何操作) )。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.