为什么在异步等待之后引发ThreadAbortException会自动重新抛出?

时间:2018-01-12 15:59:04

标签: c# .net async-await try-catch threadabortexception

在调查an issue with finally, await, and ThreadAbortException时,我又来了一个怪癖。根据{{​​3}}:

  

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

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

1 个答案:

答案 0 :(得分:2)

如果您不await Task.Yield,则会发生这样的原因:代码是在与调用方相同的线程上同步执行的,因此它根本就不是async

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

由于内部被捕获并从与当前不同的线程中重新抛出,因此它不会保留它的特性&#34; 特殊应用程序查杀&#34 ;转换逻辑中的异常。

此外,如果要重新抛出它,你甚至不能Thread.ResetAbort(),因为它适用于当前线程,并且不会对实际中止的线程起作用。

MSDN 文档也解释了这一点here

  

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

     

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

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