如果可能的話,應該避免中斷和中斷線程,因爲這會破壞正在運行的程序的狀態。例如,假設你中止了一個持有對資源開放的鎖的線程,這些鎖永遠不會被釋放。
而是考慮使用的信令機制,使線程可以互相合作,並因此處理阻塞和正常疏通,如:
private readonly AutoResetEvent ProcessEvent = new AutoResetEvent(false);
private readonly AutoResetEvent WakeEvent = new AutoResetEvent(false);
public void Do()
{
Thread th1 = new Thread(ProcessSomething);
th1.IsBackground = false;
th1.Start();
ProcessEvent.WaitOne();
Console.WriteLine("Processing started...");
Thread th2 = new Thread(() => WakeEvent.Set());
th2.Start();
th1.Join();
Console.WriteLine("Joined");
}
private void ProcessSomething()
{
try
{
Console.WriteLine("Processing...");
ProcessEvent.Set();
}
finally
{
WakeEvent.WaitOne();
Console.WriteLine("Woken up...");
}
}
更新
一個相當有趣的低級別問題。儘管Abort()
已被記錄,但Interrupt()
更是如此。
你的問題的簡短回答是否定的,你不能通過調用Abort
或Interrupt
來喚醒finally塊中的線程。
不能在finally塊中斷或中斷線程是通過設計,只是爲了讓finally塊有機會像預期的那樣運行。如果可以在finally塊中斷和中斷線程,這可能會對清除例程產生意想不到的後果,因此應用程序處於損壞狀態 - 這並不好。
線程中斷的細微差別在於,中斷可能是在線程進入finally塊之前發出的,但它並未處於SleepWaitJoin
狀態(即未被阻止)。在這種情況下,如果finally塊中存在阻塞調用,它會立即拋出ThreadInterruptedException
並崩潰在finally塊之外。最後,塊保護防止這種情況。
除了在finally塊中進行保護,它還擴展爲嘗試塊和CERs(Constrained Execution Region),它們可以在用戶代碼中進行配置,以防止在執行區域之後引發一系列異常 - 對於關鍵塊非常有用代碼必須完成並延遲中止。
這個例外(無雙關語意思)是所謂的Rude Aborts。這些是由CLR託管環境本身提出的ThreadAbortExceptions
。這些可能會導致最終和catch塊被退出,但是而不是 CERs。例如,CLR可能會提出粗魯中止以迴應它認爲花費太長時間完成工作\退出的線程。當試圖卸載AppDomain或在SQL Server CLR中執行代碼時。在您的特定示例中,當您的應用程序關閉並且AppDomain卸載時,CLR將在睡眠線程上發出Rude Abort,因爲會出現AppDomain卸載超時。
終止塊中止和中斷不會發生在用戶代碼中,但兩種情況之間的行爲稍有不同。
中止
當在最後塊上調用線程Abort
,調用線程被阻塞。這是documented:
調用中止可能會阻止如果被中止線程的代碼,一個受保護的區域,如catch塊,finally塊,或約束的執行區域的線程。
在中止情況下,如果睡眠不是無限的:
- 調用線程將發出
Abort
但直到最後退出塊來阻塞即它在這裏停止,並且不立即進行到Join
聲明。
- 被調用者線程的狀態設置爲
AbortRequested
。
- 被叫方繼續睡覺。
- 當被呼叫者醒來時,因爲它的狀態爲
AbortRequested
它將繼續執行finally塊代碼,然後「蒸發」即退出。
- 當中止的線程離開finally塊時:沒有發生異常,執行finally塊後沒有代碼,線程的狀態爲
Aborted
。
- 調用線程解除阻塞,繼續到
Join
語句,並在調用線程退出時立即通過。
所以給出一個無限睡你的榜樣,調用線程將在步驟1中
中斷
在中斷情況下,永遠阻止如果睡眠不是無限的:
沒有那麼好記錄...
- 調用線程將發出
Interrupt
和繼續執行。
- 調用線程將阻塞
Join
語句。
- 被調用者線程的狀態設置爲在下一次阻塞調用時引發異常,但是至關重要的是,它在finally塊中不會被解除阻塞,即被喚醒。
- 被叫方繼續睡覺。
- 當被調用者醒來時,它將繼續執行finally塊。
- 當中斷的線程離開finally塊時,它會在下一個阻塞調用中拋出一個
ThreadInterruptedException
(請參閱下面的代碼示例)。
- 調用線程「加入」,並繼續作爲被叫線程退出,但是,在第6步未處理
ThreadInterruptedException
現在已經變平的過程中...
所以再次與無限的睡眠給你的榜樣,調用線程將永遠阻塞,但在步驟2
摘要
因此,儘管Abort
和Interrupt
略有不同的行爲,他們都將導致被調用線程睡眠永遠,並且調用線程阻塞永遠(以你的例子)。
只有粗魯中止可以強制阻塞線程退出finally塊,而這些只能由CLR自籌(你甚至不能使用反射來騙取ThreadAbortException.ExceptionState
因爲它使內部CLR調用來獲取AbortReason
- 沒有機會在那裏輕易地邪惡......)。
CLR防止用戶代碼導致最終塊過早退出,這有助於防止損壞狀態。
對於略有不同的行爲與Interrupt
一個例子:
internal class ThreadInterruptFinally
{
public static void Do()
{
Thread t = new Thread(ProcessSomething) { IsBackground = false };
t.Start();
Thread.Sleep(500);
t.Interrupt();
t.Join();
}
private static void ProcessSomething()
{
try
{
Console.WriteLine("processing");
}
finally
{
Thread.Sleep(2 * 1000);
}
Console.WriteLine("Exited finally...");
Thread.Sleep(0); //<-- ThreadInterruptedException
}
}
我不知道這個問題的答案,但我確實是會知道,作爲一般規則,我儘量避免'中斷( )'作爲線程間信號的一種機制。還有更多可預測的API--監視器,{手動|自動} ResetEvent'等 –
當Interrupt/Abort最終不能工作時,我更多地尋找任何解釋/文檔,爲什麼MSDN會說相反。我的例子是更復雜的輪詢類型,我現在正在閱讀它,等待處理的建議。 – Marek
有兩種線程中止。友好的人不會中斷代碼塊。當進程在IsBackground等於true的線程上關閉時,或者當它終止於未處理的異常時,將調用不友好的進程。現在代碼被粗暴地打斷並不重要。 –