3

As a result of investigating this question: Rethrowing exception in Task doesn't make the Task to go to faulted state, I noticed some very odd behavior with the ThreadAbortException that I can't make sense of.

Now, I know that ThreadAbortException is a very special kind of exception to begin with. And the documentation is pretty clear about that when it says:

ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block.

Scenario #1: Documented behavior.

static void Main(string[] args)
{
    try
    {
        Thread.CurrentThread.Abort();
    }
    catch (Exception tae)
    {
        Console.WriteLine("caught exception: " + tae.GetType().Name);
    }
    Console.WriteLine("will never be reached");
}

As expected, the ThreadAbortException is rethrown automatically, resulting in the following output:

caught exception: ThreadAbortException

Scenario #2: Where it gets interesting is when I decide to throw a different exception in the catch block:

static void Main(string[] args)
{
    try
    {
        Thread.CurrentThread.Abort();
    }
    catch (Exception tae)
    {
        Console.WriteLine("caught exception: " + tae.GetType().Name);
        throw new ApplicationException(); // will ThreadAbortException take precedence?
    }
    Console.WriteLine("will never be reached");
}

In this case, I assumed that, despite the throwing of the ApplicationException, that the ThreadAbortException would be rethrown anyways to ensure that the documented behavior was preserved. To my surprise, this is the output that resulted:

caught exception: ThreadAbortException

Unhandled Exception: System.ApplicationException: Error in the application.
   at ConsoleApplication1.Program.Main(String[] args) in C:\projects\ConsoleApplication1\Program.cs:line 193

Did the ApplicationException actually replace and prevent the ThreadAbortException from being thrown?!?

Scenario #3: And finally, to make matters more interesting, I wrap my existing exception handling with one more try-catch layer:

static void Main(string[] args)
{
    try
    {
        try
        {
            Thread.CurrentThread.Abort();
        }
        catch (Exception tae)
        {
            Console.WriteLine("caught exception: " + tae.GetType().Name);
            throw new ApplicationException();
        }
    }
    catch (Exception outerEx)
    {
        Console.WriteLine("caught outer exception: " + outerEx.GetType().Name);
    }
    Console.WriteLine("will never be reached");
}

Now I'm not too sure what to expect. Which exception will be caught in the outer catch block? Will it be ApplicationException? And if so, does that mean that I'll be able to swallow the exception and actually manage to print out the will never be reached string after all?

This is the actual output:

caught exception: ThreadAbortException
caught outer exception: ApplicationException

From the above output, it looks like the outer catch block does actually catch an ApplicationException. But by the end of that catch block, the ThreadAbortException now all of a sudden reappears out of thin air and gets rethrown?

Question(s): Can someone explain and reconcile scenarios #2 and #3? How is it that in scenario #2, it looks as though the ThreadAbortException is, unexpectedly, replaced by a different exception? Yet, in scenario #3, it looks like ThreadAbortException was still there all along? How is this happening? Is this behavior documented somewhere?

4

1 回答 1

1

该行为是特定于实现的。

来自Ecma-335 VI Annex E,可移植性考虑

本条款收集了有关本标准故意为实施留出余地的领域的信息。这种余地旨在允许兼容的实现做出提供更好性能或以其他方式增加价值的选择。但是这种余地固有地使程序不可移植。...

以及进一步(强调我的):

六、E。1 无法控制的行为

程序行为的以下方面取决于实现。>其中许多项目对于习惯于编写专为 > 可移植性而设计的代码的程序员来说是熟悉的(例如,CLI 没有为堆或堆栈规定最小大小的事实)。

  1. 堆和堆栈的大小不需要具有最小大小

与异步异常相关的行为(参见 System.Thread.Abort)

不支持全球化,因此每个实现都指定其文化信息,包括用户可见的特征,如字符串的排序顺序。

不能假定线程被抢占式或非抢占式调度。这个决定是特定于实现的。

定位程序集是一种特定于实现的机制。

安全策略是一种特定于实现的机制。

文件名是特定于实现的。

定时器分辨率(粒度)是特定于实现的,尽管指定了单位。

于 2015-07-23T02:18:55.650 回答