3

在调查, 和 ThreadAbortException的问题时finallyawait我遇到了另一个怪癖。根据文档

ThreadAbortException 是一个可以被捕获的特殊异常,但它会在catch块结束时自动再次引发。

但是考虑这个控制台程序:

class Program
{
    static void Main()
    {
        Run(false).GetAwaiter().GetResult();
        Run(true).GetAwaiter().GetResult();
    }

    static async Task Run(bool yield)
    {
        Console.WriteLine(yield ? "With yielding" : "Without yielding");
        try
        {
            try { await Abort(yield); }
            catch (ThreadAbortException)
            {
                Console.WriteLine("    ThreadAbortException caught");
            } // <-- ThreadAbortException should be automatically rethrown here
        }
        catch (ThreadAbortException)
        {
            Console.WriteLine("    Rethrown ThreadAbortException caught");
            Thread.ResetAbort();
        }
    }

    static async Task Abort(bool yield)
    {
        if (yield)
            await Task.Yield();
        Thread.CurrentThread.Abort();
    }
}

当我在 Visual Studio 2015 中编译它时,输出是:

Without yielding
    ThreadAbortException caught
    Rethrown ThreadAbortException caught
With yielding
    ThreadAbortException caught

因此,之后引发的 ThreadAbortExceptionTask.Yield()不再被catch块自动重新抛出!为什么是这样?

4

1 回答 1

2

如果你不这样做,它发生的原因await Task.Yield是代码是在与调用者相同的线程上同步执行的,所以它就像根本不存在一样async

当 youawait时,延续将在一个ThreadPool线程上排队,该线程是一个托管线程并且行为不同。

由于在内部被捕获并从与当前线程不同的线程重新抛出,因此它不会在转换逻辑中保留其“特殊应用程序终止”异常的性质。

此外,如果要重新抛出它,您甚至无法Thread.ResetAbort()在当前线程上工作,并且不会对实际中止的线程采取行动。

MSDN文档在这里也解释了这一点:

如果在公共语言运行时创建的线程中未处理这些异常中的任何一个,则异常会终止线程,但公共语言运行时不允许异常继续进行。

如果这些异常在主线程中或在从非托管代码进入运行时的线程中未处理,它们将正常进行,从而导致应用程序终止。

我对这背后的基本原理的猜测是:ThreadAbortException被重新抛出以避免顽固的线程在不应该的时候尝试保持活力,但是让它流动并杀死其他不同的线程可能是一个非常糟糕的主意,并导致意外的行为。

于 2018-01-12T16:16:23.653 回答