【问题标题】:.NET Thread.Abort again.NET Thread.Abort 再次
【发布时间】:2010-01-18 09:30:48
【问题描述】:

我想再次谈谈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 的数量略少,因为一些线程在没有中止的情况下完成或最终可能被中止。

所以问题是,我错过了什么吗?

【问题讨论】:

    标签: .net multithreading abort


    【解决方案1】:

    通过人为的测试,您可以证明任何事情。

    您所证明的只是 您为测试编写的代码Thread.Abort 似乎可以正常工作。

    然而,问题是,一旦你开始使用需要处理的东西,所有的希望都落空了。

    例如,试试这个代码:

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

    现在,运行一段时间,然后告诉我它是否仍然有效。

    当代码离开你的睡眠调用,并且在 using 块的隐式 finally 块内,并且正要关闭你的流,然后你中止它时,就会出现问题。

    Thread.Abort 的问题在于它可能发生在任何地方,即使在不应该引发异常的代码中也是如此。

    例如,您是否真的希望以下代码在 if 表达式被评估之后,但 Dispose-调用已经完成之前 崩溃?

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

    如果它在调用.Dispose 之后立即发生怎么办?该字段仍将具有非空值,这可能会导致其他地方出现微妙的问题。

    如果你这样做会怎样:

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

    使用此代码,您获取值,将其替换为 null,然后在您开始调用 Dispose 之前,您的 ThreadAbortException 发生了,这将离开对象。

    让我把重点带回家:

    Thread.Abort 应该永远被使用,除非在您需要终止程序(或拆除其中运行线程的自定义 AppDomain)的情况下。您应该切勿调用 Thread.Abort 然后继续运行。

    除非您需要将错误计划到您的未来计划中。在这种情况下,直接使用Thread.Abort,因为我几乎可以保证你会遇到问题。

    【讨论】:

    • 我尝试运行您在打开文件时提到的代码,是的,在创建新线程后的某个时间点,文件仍然忙于另一个应该被中止的线程,但是如果线程不会使用怎么办共享资源?似乎它只是证明线程 Abort 有点不安全。即使我只需要终止应用程序,如果某些非托管代码正在运行,Abort 也不会导致进程挂起或内存泄漏?在我的情况下,我需要一些可以将进程运行时间限制为精确超时限制的东西,而在我的情况下,我无法控制这个进程的执行。对这个案例有什么想法吗?
    • 您应该对 SO 提出不同的问题,即如何限制执行时间,然后在该代码中发布您打算执行的操作。例如,在某些情况下,如果代码忙于非托管代码,则无法执行您想要的操作。唯一“安全”的方法是生成一个在超时结束后完全终止的子进程。
    • 小修正(即使这个答案已经有一年多了)线程在 catch 和 finally 块中不能中止。所以假设一个写得很好的 finally 块或使用 using 块,上面的例子不是真正的问题。
    【解决方案2】:

    使用线程中止是足够安全的。但是,正如其他人所提到的,线程中止可能不会立即中止。调用线程中止将在线程中引发 ThreadAbortException。要清理资源,您可以捕获此异常并进行必要的清理。

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

    【讨论】:

    • 第二次将需要清理的资源引入线程,Thread.Abort 就不够安全了。
    • 显然,问题在于 Thread.Abort 不够聪明,无法知道您的代码实际上可能位于 finally 块中,并且会直接退出该块。如果只有它可以告诉并且仅在代码不在 catch/finally 中时中止并等待它退出块...
    【解决方案3】:

    使用 Thread.Abort 很好。但是,它并不总是立即中止。如果一个线程正在执行非托管代码,它实际上不会中止,直到它返回到托管代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-12
      • 1970-01-01
      • 2011-08-05
      • 2013-05-30
      • 2023-03-23
      • 2012-03-27
      • 1970-01-01
      相关资源
      最近更新 更多