2010-01-18 44 views
1

我再次想談談Thread.Abort函數的安全性。我有興趣有一些方法可以放棄我實際上無法控制的操作,但我希望儘快釋放線程以防止線程對我的應用程序感到厭煩。.NET Thread.Abort再次

所以我寫了一些測試代碼,看看是否有可能使用Thread.Abort並讓異常中止的線程清理資源。這裏是代碼:

int threadRunCount = 0; 
int threadAbortCount = 0; 
int threadFinallyCount = 0; 
int iterations = 0; 

while(true) 
{ 
Thread t = new Thread(() => 
{ 
    threadRunCount++; 
    try 
    { 
    Thread.Sleep(Random.Next(45, 55)); 
    } 
    catch(ThreadAbortException) 
    { 
    threadAbortCount++; 
    } 
    finally 
    { 
    threadFinallyCount++; 
    } 
}); 

t.Start(); 
Thread.Sleep(45); 
t.Abort(); 

iterations++; 
} 

所以,到目前爲止,這個代碼工作約5分鐘,並threadRunCount總是等於threadFinallythreadAbort在數量上有所回落,因爲一些線程完成,沒有中止或很可能在最後得到了中止。

所以問題是,我想念什麼?

回答

4

通過人爲測試,您可以證明任何事情。

所有你已經證明的是,你爲你的測試編寫的代碼Thread.Abort似乎工作正常。

然而問題是,只要你開始使用需要處理的東西,所有的希望都會丟失。

例如,試試這個代碼:

using (Stream stream = new FileStream(@"C:\Test.txt", FileMode.Create, FileAccess.ReadWrite, FileShare.None)) 
{ 
    Thread.Sleep(Random.Next(45, 55)); 
} 

現在,運行了一會兒,並告訴我,如果目前仍然可以工作。

當代碼已經離開您的睡眠呼叫,並且位於using-block的隱式finally塊內,並且即將關閉您的流,然後中止它時,問題就會出現。

Thread.Abort的問題是,它可以發生在任何地方,即使在原本不拋出異常的代碼。

舉例來說,你真的希望下面的代碼的,如果表達式已被評估之後崩潰,但Dispose -call已經通過?

if (_ObjectToDispose != null) 
{ 
    _ObjectToDispose.Dispose(); 
    _ObjectToDispose = null; 
} 

如果在.Dispose的電話後立即發生了什麼呢?該字段仍將具有非空值,這可能會導致其他地方出現細微問題。

如果你這樣做是什麼:

IDisposable objectToDispose = Interlocked.Exchange(ref _ObjectToDispose, null); 
if (objectToDispose != null) 
    objectToDispose.Dispose(); 

有了這個代碼,你搶的值,用空替換它,然後你避開調用Dispose之前,您ThreadAbortException發生,這將只是離開目的。

讓我在開車回家點:

Thread.Abort的應該從未被使用,除非你需要終止程序(或推倒一個自定義的AppDomain與正在運行的線程場景它)。你應該從來沒有調用Thread.Abort,然後繼續運行。

除非您需要將錯誤計劃到您的未來計劃中。在這種情況下,請繼續前進並使用Thread.Abort,因爲我幾乎可以保證您會遇到問題。

+0

我試圖運行代碼,您提到了文件打開,並且是在創建新線程之後的某個時間點文件仍然忙於另一個應該被中止的線程,但是如果線程不使用共享資源呢? 似乎它只是證明線程Abort有點不安全。 即使我只是需要終止應用程序無法中止導致處理掛起或一些內存泄漏,如果一些非託管代碼正在運行? 在我的情況下,我需要的東西可以限制進程運行時間到確切的超時限制,在我的情況下,我無法控制此進程的執行。對此案件的任何想法? – hoodoos 2010-01-18 11:20:37

+0

您應該在SO上提出一個不同的問題,如何限制執行時間,然後在該代碼中發佈您打算執行的操作。在某些情況下,例如,如果代碼在非託管代碼中處於忙碌狀態,則無法做到您想要的操作。唯一的「安全」方法是產生一個在超時過後完全終止的子進程。 – 2010-01-18 11:24:47

+2

小修正(即使這個答案已經過了一年) 線程不會在catch和finally塊內部放棄。所以假設一個寫得很好的finally塊或者使用塊,上面的例子並不是真正的問題。 – Steve 2011-05-16 04:58:58

-1

使用Thread.Abort很好。但是,它並不總是立即中止。如果一個線程正在執行非託管代碼,它將不會實際中止,直到它返回到託管代碼。

1

使用線程中止足夠安全。但正如其他人所說,線程中止可能不會立即中止。調用線程中止會在線程中產生ThreadAbortException。要清理資源,您可以捕獲此異常並執行必要的清理。

static void Run() 
{ 
    try 
    { 
    while(someCondition) 
    { 
     .... 
     .... 
     .... 
     if (someOtherCondition) 
      throw new ThreadAbortException("Thread aborted"); 
    } 
    } 
    catch(ThreadAbortException e) 
    { 
    ... 
    ... //clean up resources here. 
    ... 
    } 
    finally 
    { 
    ... 
    } 
} 
+0

第二種是將需要清理的資源引入到線程中,Thread.Abort * *不夠安全。 – 2010-01-18 10:53:02

+0

顯然,問題在於Thread.Abort不夠聰明,不知道你的代碼實際上可能在'finally'塊中,並且會立即退出該塊。如果只有它可以告訴並且只有在代碼不在catch/finally並等到它退出塊時纔會中止... – devios1 2011-07-16 04:32:00