2010-01-18 61 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