c# - 如果在异步等待后引发,为什么 ThreadAbortException 不会自动重新抛出?

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

调查时an issue with finally, await, and ThreadAbortException ,我又来了一个怪癖。根据documentation :

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

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

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() 之后引发的 ThreadAbortException 不再由 catch block 自动重新抛出!这是为什么?

最佳答案

如果你不 await Task.Yield 会发生这种情况的原因是代码是在与调用者相同的线程上同步执行的,所以它就像不是 async 完全没有。

当您await 时,continuation 将在 ThreadPool 线程上排队,该线程是托管线程并且行为不同。

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

此外,如果要重新抛出它,您甚至无法 Thread.ResetAbort(),因为它在当前线程上工作,并且不会作用于那个线程实际上中止了。

MSDN 文档对此也有解释 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.

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

关于c# - 如果在异步等待后引发,为什么 ThreadAbortException 不会自动重新抛出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48229821/

相关文章:

c# - Entity Framework 5 日志数据库在运行时发生变化

c# - 重 I/O 操作中的 Parallel.ForEach 与异步 For 循环

c# - 异步执行与预期不同

javascript - 异步代码和 mongodb 的问题

c# - 从图像上绘制的文本中删除顶部和底部填充

javascript - 拾取和移动物体

c# - Dispatcher.Invoke 是否在调用线程上阻塞?

c# - SignalR 2.0 错误 : Could not load file or assembly Microsoft. Owin.Security

c# - Entity Framework - 为集合中的成员加载特定的导航属性

c# - "iPhone"的 Resharper 命名异常