【问题标题】:ThreadAbortException线程中止异常
【发布时间】:2010-12-23 19:11:06
【问题描述】:

假设我们有一些这样的代码在单独的线程中运行:

private static void ThreadFunc() {
    ulong counter = 0;

    while (true) {

        try {
            Console.WriteLine( "{0}", counter++ );
        }
        catch (ThreadAbortException) {
            Console.WriteLine( "Abort!" );
        }

    }
}

Thread.Abort()被调用时,是否有可能在catch块之外抛出异常?

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    实际上是的,ThreadAbortException 很特别。即使你处理了它,它也会在 try/catch/finally 结束时被 CLR 自动重新抛出。 (如 cmets 中所述,可以使用 ResetAbort 抑制它,但此时代码闻起来像烂鱼。)

    更不用说,即使在 try/catch/finally 之外没有明显的可执行代码,循环的每次迭代都会在短时间内超出范围,因此中止可能发生在 try 块之外。

    除非你真的在 catch 块中做某事,否则我只会尝试/最终,不要担心ThreadAbortException。有很多更好的方法可以在不使用Thread.Abort 的情况下中止线程,这不仅会在不可预知的点混乱地中断您的代码,而且也不能保证工作,因为如果您的线程当前正在调用一些非托管代码,线程将不会中止直到控制权返回到托管代码。

    最好使用某种类型的同步原语(例如ManualResetEvent)作为一个标志来告诉您的线程何时退出。您甚至可以为此目的使用一个布尔字段,这正是 BackgroundWorker 所做的。

    【讨论】:

    • +1。只是不要中止线程,这几乎不是一个好主意。
    • 您可以使用 Thread.ResetAbort() 来阻止它被重新抛出,不是吗?
    • 当您执行此操作时,您的代码闻起来像烂鱼,这是绝对正确的。但是,ASP.net uses the it to implement Response.Redirect,当然是使用Thread.ResetAbort 重置的,所以在这种情况下它们可能会发生。对这种情况没有帮助的是 .NET 框架 isn't hardened against asynchronous exceptions 所以即使 ASP.net 使用它们,它仍然是一个非常糟糕的主意。
    • @PietervanGinkel,我认为最大的区别在于Response.Redirect 不会异步中止另一个线程;它中止自身。这并不是真正的异步线程中止。这就是为什么它是一种安全的方法。此外,它没有太多选择。无论客户端代码是什么样子,它都必须以某种方式完全中断处理,展开堆栈并将线程返回到线程池。这几乎是唯一的方法。
    【解决方案2】:

    是的。我怀疑您问的是因为线程中断仅在线程可能阻塞(或者如果它已经被阻塞)时发生 - 例如用于 IO。

    没有这样的中止保证。它可以在任何时候发生,基本上,尽管有 delay-abort 区域,例如 constrained execution regions 和 catch/finally 块,其中只记住中止请求,并且线程在退出时中止地区。

    同步线程中止(即中止您自己的线程)相当安全,但异步中止(中止不同线程)几乎总是一个坏主意。阅读"Concurrent Programming on Windows" by Joe Duffy了解更多信息。

    编辑:正如下面 Eric 所指出的,中止另一个线程也不能保证实际上有任何效果。只是引用评论:

    我会说线程被中止如果它退出该区域,强调Thread.Abort 是完全不可靠的。如果循环在这样的区域中,则由于陷入无限循环而中止的线程将不会中止。这也是为什么Thread.Abort 是个坏主意的另一个原因。如果您不能依赖实际发生的预期效果,那您为什么要调用该方法?

    【讨论】:

    • 很好的回答乔恩,我唯一的评论是我会说线程被中止如果它退出该区域,强调Thread.Abort 是完全不可靠的。如果循环在这样的区域中,则由于陷入无限循环而中止的线程将不会中止。这也是为什么Thread.Abort 是个坏主意的另一个原因。如果您不能依赖实际发生的预期效果,那您为什么要调用该方法?
    【解决方案3】:

    实际上,ThreadAbortException 是特殊的,以防它被 CLR 或 Thread.Abort 方法抛出。比较输出:

    • 对 Joe Duffy 的“Windows 上的并发编程”中的示例进行了略微修改(添加了 Console.WriteLine)。它通过 Thread.CurrentThread.Abort 抛出异常: try { try { Thread.CurrentThread.Abort(); } catch (ThreadAbortException) { Console.WriteLine("First"); //Try to swallow it. } //CLR automatically reraises the exception here . } catch (ThreadAbortException) { Console.WriteLine("Second"); Thread.ResetAbort(); //Try to swallow it again . } //The in - flight abort was reset , so it is not reraised again . 输出: 第一的 第二
    • 修改前面的示例以使用不同的 ThreadAbortException 实例创建方法:
      
      try
      {
          try
          {
              // get non-public constructor
              var cstor = typeof(ThreadAbortException)
                  .GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
              // create ThreadAbortException instance
              ThreadAbortException ex = cstor.Invoke(null) as ThreadAbortException;
      
              // throw..
              throw ex;
          }
          catch (ThreadAbortException)
          {
              Console.WriteLine("First");
          } 
      }
      catch (ThreadAbortException)
      {
          Console.WriteLine("Second");
          Thread.ResetAbort();
      } 
      
      输出: 第一的

    似乎 线程的 方法在内部调用本机代码,这使得引发的异常是特定的。

    【讨论】:

    • 是的,它是特定的......这正是 MSDN 所说的。您可以捕获与 JustMyCode 相关的异常,然后 Thread.ResetAbort() 将正确处理它。 thread.Abort() 的 try/catch 包装并不特定于异常。因此,您不需要包装它,因为它很特殊。你上面的模型是错误的,没有意义。您只需要包装从相关线程中抛出异常的委托部分......一次......就是这样!
    【解决方案4】:

    我不是 100% 你在问什么,但我想指出你永远无法吞下ThreadAbortException

    当调用 Abort 时 销毁线程的方法,常见的 语言运行时抛出一个 ThreadAbortExceptionThreadAbortException 是一个特殊的 可以捕获的异常,但它 将在 catch 块的结尾。

    您是不是在问是否有可能用try/catch 捕获在另一个线程中抛出的ThreadAbortException?如果这是你的问题,那么不,你不能。

    【讨论】:

    • 调用 ResetAbort 将阻止 CLR 重新抛出异常。
    猜你喜欢
    • 2011-04-15
    • 2011-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多