【问题标题】:Revisiting Thread.Abort() - is it safe?重温 Thread.Abort() - 安全吗?
【发布时间】:2011-08-16 15:48:11
【问题描述】:

关于迁移遗留多线程应用程序的 MSDN(来自关于线程异常处理的 this 页面):

一般来说,更改将暴露以前无法识别的编程问题,以便可以修复它们。然而,在某些情况下,程序员可能会利用运行时支持,例如终止线程。根据情况,他们应该考虑以下迁移策略之一:

重构代码,以便线程在收到信号时优雅地退出。

使用 Thread.Abort 方法中止线程。

如果必须停止线程才能继续进程终止,请将线程设置为后台线程,以便在进程退出时自动终止。

在所有情况下,策略都应遵循例外设计指南。请参阅例外设计指南。

这表明使用Thread.Abort 是终止线程的合适方法。在我不看的时候有什么变化吗?我最后听到的是这可能会导致意外行为,因此不应该使用。

【问题讨论】:

标签: c# .net multithreading


【解决方案1】:

Thread.Abort 比以前更安全,原因如下。

  • 在非托管代码中执行时,运行时将推迟中止。
  • 中止将允许执行finally 块。

但是,ThreadAbortException 被注入的确切时间仍然存在问题。考虑这段代码。

public class Example
{
  private DateTime value = DateTime.MinValue;

  public void DoSomething()
  {
    try
    {
      value = DateTime.UtcNow;
    }
    finally
    {
    }
  }
}

如果此代码在 32 位平台上运行,则在调用 Thread.Abort 并且在写入 value 的过程中注入 ThreadAbortException 时,value 变量可能会损坏。由于DateTime 是 8 个字节,因此必须使用多条指令进行写入。

可以通过将关键代码放在finally 块中并使用Constrained Execution Regions 来防止这种情况发生,但是除了您定义的最简单类型之外,要正确处理所有类型将非常困难。即便如此,您也不能只将 everything 放在 finally 块中。

【讨论】:

  • 另一种看待它的方式:Thread.Abort 的用处比以前少了很多,原因如下:在非托管代码中延迟中止,最后阻塞执行。如果您因为线程正在执行不需要的工作而中止线程,那么这两件事都会导致线程继续执行不需要的工作。
  • @Eric:是的,有趣的观点。我没有考虑那个角度。
【解决方案2】:

一般来说,Thread.Abort 会杀死线程,使它们当时正在处理的数据处于未知状态。状态未知,处理这些数据通常是不安全的。但是,当您尝试终止一个进程时,您不再期望处理该线程的数据,那么为什么不中止它呢?

【讨论】:

  • Thread.Abort 将抛出一个异常并(尝试)执行所有 finally 块。没有那么多未知的状态。
【解决方案3】:

嗯,Thread.Abort() 的问题在于,它可能会在工作过程中中止线程。这可能会导致您的状态被破坏。这就是为什么建议使用 volatile bool 标志来控制线程,并让线程优雅地完成其任务,但基于该标志。

想了解更多详情,我记得this blog post

【讨论】:

  • 请注意该帖子上的日期,它是关于 Fx 1.1
  • @Henk:不错的收获。中止的行为在 2.0 中发生了很大变化。