简体   繁体   中英

Why isn't ThreadAbortException automatically rethrown if raised after an asynchronous await?

While investigating an issue with finally , await , and ThreadAbortException , I came another quirk. According to the documentation :

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

But consider this console program:

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();
    }
}

When I compile this in Visual Studio 2015, the output is:

Without yielding
    ThreadAbortException caught
    Rethrown ThreadAbortException caught
With yielding
    ThreadAbortException caught

So a ThreadAbortException raised after Task.Yield() is no longer automatically rethrown by a catch block! Why is this?

The reason why it happens if you don't await Task.Yield is that the code is executed synchronously on the same thread as the caller so it's like not being async at all.

When you await , the continuation will be queued on a ThreadPool thread which is a managed thread and behaves differently.

Since internally is caught and re-thrown from a different thread than the current, it doesn't preserve it's nature of " special application killing " exception in the shifting logic.

In addition, if it was to re-throw it, you wouldn't even been able to Thread.ResetAbort() as it works on the current thread and will not act on the one that actually aborted.

MSDN documentation explains this, too here :

If any of these exceptions are unhandled in threads created by the common language runtime, the exception terminates the thread, but the common language runtime does not allow the exception to proceed further.

If these exceptions are unhandled in the main thread, or in threads that entered the runtime from unmanaged code, they proceed normally, resulting in termination of the application.

My guess on the rationale behind this is: ThreadAbortException is re-thrown to avoid stubborn threads to try and stay alive when they shouldn't, but letting it flow and kill also other different threads would probably be quite a bad idea and cause unexpected behaviour.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM