为什么ThreadAbortException不抛出catch块

本文关键字:catch ThreadAbortException 为什么 | 更新日期: 2023-09-27 18:10:13

假设我有以下代码:

    static void Main(string[] args)
    {
        var thread = new Thread(() =>
        {
            try
            {
                throw new InvalidOperationException();
            }
            catch (Exception)
            {
                Thread.Sleep(Timeout.Infinite);
            }
        });
        thread.Start();
        Thread.Sleep(TimeSpan.FromSeconds(1));
        thread.Abort();
        thread.Join();
    }

它启动线程,然后线程在catch块中进入睡眠状态,之后我们尝试中止线程。

Abort方法必须引发ThreadAbortException。但是在catch块中不会发生这种情况。记录:

调用Abort的线程可能会阻塞Aborted位于代码的受保护区域,例如catch块;最后块,或约束执行区域。如果这个线程调用Abort持有被终止线程所需的锁,即死锁能发生。

我的问题是为什么。为什么会这样呢?因为在catch块中,我们可以引发任何异常,并且所有的工作都像它必须的那样。

:来自jord的链接。接受,因为这是最容易理解的解释。

.NET Framework 2.0引入的受限执行区域约束执行区域(CER),它对运行时和开发人员。在标记为CER的代码区域中,运行时被限制不能抛出某些异步异常将阻止该区域在其完整。开发人员在可以执行的操作中也受到限制在该地区执行。这创建了一个框架和执行编写可靠托管代码的机制,使其成为关键角色在。net Framework 2.0的可靠性故事中。对于运行时为了满足其负担,它对CERs做出了两项调整。首先,运行时将延迟正在CER中执行的代码的线程中止。换句话说,如果一个线程调用thread。Abort终止另一个线程如果当前正在CER内执行,则运行时将不会中止目标线程,直到执行离开CER。第二,运行时将尽快准备cer以避免出现内存不足的情况。这意味着运行时就可以了它在代码区域通常会做的所有事情JIT编译。它还将探测一定数量的空闲堆栈帮助消除堆栈溢出异常的空间。通过做这项工作预先,运行时可以更好地避免可能发生的异常在区域内,防止资源被清理适当。为了有效地使用cer,开发人员应该避免可能导致异步异常的某些操作。的代码是否被限制执行某些操作,包括显式分配,装箱,虚拟方法调用(除非目标(虚方法调用已经准备好了),方法调用通过反射,使用Monitor。输入(或c#中的lock关键字)和SyncLock在Visual Basic®),isint和castclass指令COM对象,通过透明代理访问字段,序列化,以及多维数组访问。简而言之,cer是一种移动方式从代码到某个时间点的任何运行时引起的故障点在代码运行之前(在JIT编译的情况下),或者在代码运行之后完成(对于线程中止)。然而,CERs确实限制了你能写的代码。限制,例如不允许大多数分配或者对未准备目标的虚拟方法调用很重要,这意味着编写它们的开发成本很高。这意味着cer不适合大型的通用代码体,而且它们应该把它看作是一种保证执行的技术吗小区域代码。

为什么ThreadAbortException不抛出catch块

问题是您试图中止的线程在catch子句中运行。

这将终止线程:

static void Main(string[] args) {
  var thread = new Thread(() => {
    Thread.Sleep(Timeout.Infinite);
  });
  thread.Start();
  Thread.Sleep(TimeSpan.FromSeconds(1));
  thread.Abort();
  thread.Join();
}

摘自本文:

在。net Framework 2.0中,默认情况下,CLR延迟优雅线程终止,包括cer、finally块、catch块、静态构造函数和非托管代码。

这个特性的存在是为了使。net框架在面对某些异步异常时更可靠。请阅读我链接的文章以获取完整的故事。

你的代码基本上是错误的行为,主机可能会将该线程升级为一个粗鲁的线程中止:

CLR主机使用粗线程中止和粗应用程序域卸载来确保失控代码可以保持在检查中。当然,由于这些操作而无法运行终结器或非cer finally块会给CLR主机带来新的可靠性问题,因为这些操作很有可能会泄漏回退代码应该清理的资源。

这是设计的,这是在Fx 3或4中引入的
您可以在自己的链接中查找不同的版本,并找到不同的描述。

在这些受保护的区域内允许AbortException(如Fx 1.x)可能导致非常不可预测的情况和不稳定的进程。

注意Thread.Abort()通常是不被建议的。任何catch或finally子句中的长时间运行代码也是如此。

禁止Abort来中断catch子句可以解决Abort的一些问题。但它仍然不完美。

怀疑问题是,当你在catch或最终阻塞时,你可能已经在努力清理自己了。如果此时触发异步异常,将很难进行任何可靠的清理。

Joe Duffy关于异步异常的博文可能比我更清楚地说明了这一点。