为什么ThreadAbortException不会抛出catch块

时间:2011-10-03 12:10:51

标签: c# multithreading try-catch

假设我有这段代码:

    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区块中它不会发生。 这是documented

  

调用Abort的线程可能会阻塞正在进行的线程   中止是在受保护的代码区域中,例如catch块,   最后阻塞或约束执行区域。如果线程那个   调用Abort持有一个被中止线程需要的锁,一个死锁   可能会发生。

我的问题是为什么。为什么这样工作?因为在catch块中我们可以提出任何异常,并且所有工作都必须如此。

更新: 来自Jordão的link。接受是因为这是最容易理解的澄清。

  

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

3 个答案:

答案 0 :(得分:7)

问题是您尝试中止的线程正在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();
}

来自this article

  

在.NET Framework 2.0中,CLR默认优先于CERs,最后是块,catch块,静态构造函数和非托管代码来延迟优雅的线程中止。

此功能的存在是为了使.NET框架在面对某些异步异常时更加可靠。阅读我为完整故事链接的文章。

您的代码基本上行为不端,主机可能会将该线程升级为粗鲁的线程中止:

  

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

答案 1 :(得分:1)

怀疑关键在于,当你陷入困境或最后阻止时,你可能已经在尝试自己清理了。如果此时可以触发异步异常,那么进行任何可靠的清理都会非常困难。

乔·达菲的blog post about asynchronous exceptions可能会比我更清楚地澄清这一点......

答案 2 :(得分:1)

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

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

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

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