C# 为什么后台线程中未处理的异常不会使应用程序域崩溃?

C# 为什么后台线程中未处理的异常不会使应用程序域崩溃?,c#,.net,multithreading,exception-handling,delegates,C#,.net,Multithreading,Exception Handling,Delegates,我完全困惑不解。我非常确信,如果线程中存在未捕获的异常,那么.NET将关闭整个应用程序域,而我从未测试过这个线程 然而,我只是尝试了以下代码,它没有失败。。。谁能解释一下原因吗 (在.NET 4和3.5中试用) 因为给定线程上的异常抛出保持在那里,除非它被引导回主线程 这就是backgroundWorker为您所做的,如果您在backgroundWorker的线程中有异常,它将在主线程上重新启动 a.BeginInvoke(null, null); 这将进行异步调用,从而创建另一个线程从MSD

我完全困惑不解。我非常确信,如果线程中存在未捕获的异常,那么.NET将关闭整个应用程序域,而我从未测试过这个线程

然而,我只是尝试了以下代码,它没有失败。。。谁能解释一下原因吗

(在.NET 4和3.5中试用)


因为给定线程上的异常抛出保持在那里,除非它被引导回主线程

这就是backgroundWorker为您所做的,如果您在backgroundWorker的线程中有异常,它将在主线程上重新启动

a.BeginInvoke(null, null);
这将进行异步调用,从而创建另一个线程从MSDN上执行此操作:

在.NET Framework 2.0版中 公共语言运行库允许大多数 到的线程中未处理的异常 自然进行。在大多数情况下 表示未处理的异常 导致应用程序终止

这是与过去相比的一个重大变化 .NET Framework版本1.0和1.1, 这为许多人提供了后盾 未处理的异常-例如, 线程池中未处理的异常 线程。请参阅与上一页的更改 本主题后面的版本

作为一项临时的兼容性措施, 管理员可以放置 中的兼容标志 申请书的章节 配置文件。这导致了 要还原到的公共语言运行库 版本1.0和1.1的行为

<legacyUnhandledExceptionPolicy enabled="1"/>


发生这种情况的原因是
BeginInvoke
在内部使用
ThreadPool
,并且当
ThreadPool
时,任何未处理的异常都将失败。但是,如果使用
a.EndInvoke
,则未处理的异常将在
EndInvoke
方法中抛出


注:正如João Angelo所述,直接使用
ThreadPool
方法“如
ThreadPool.QueueUserWorkItems
UnsafeQueueUserWorkItem
”将在2.0及以上版本引发异常。

通常情况下,对于异步委托,如果委托方法引发异常,则线程将终止,并且仅在调用
EndInvoke
时才会在调用代码中再次引发异常

static void Throws()
{
    Console.WriteLine("Thread: {0}", Thread.CurrentThread.ManagedThreadId);

    throw new ApplicationException("Test 1");
}

[OneWay]
static void ThrowsButIsIgnored()
{
    Console.WriteLine("Thread: {0}", Thread.CurrentThread.ManagedThreadId);

    throw new ApplicationException("Test 2");
}

static void Main(string[] args)
{
    Console.WriteLine("Main: {0}", Thread.CurrentThread.ManagedThreadId);

    var willIgnoreThrow = new Action(ThrowsButIsIgnored);
    var result1 = willIgnoreThrow.BeginInvoke(null, null);

    Console.ReadLine();
    willIgnoreThrow.EndInvoke(result1);

    Console.WriteLine("============================");

    var willNotIgnoreThrow = new Action(Throws);
    var result2 = willNotIgnoreThrow.BeginInvoke(null, null);

    Console.ReadLine();
    willNotIgnoreThrow.EndInvoke(result2);
}
这就是为什么在使用异步委托(
BeginInvoke
)时,您应该始终调用
EndInvoke
。此外,这不应与
Control.BeginInvoke
混淆,后者可以以一种“即发即忘”的方式调用

前面我说的是“正常”,因为您可能会声明,如果委托方法返回void,那么应该忽略异常。为此,您需要使用
OneWay
属性标记该方法

如果运行以下示例,则只有在调用
willNotIgnoreThrow.EndInvoke
时才会出现异常

static void Throws()
{
    Console.WriteLine("Thread: {0}", Thread.CurrentThread.ManagedThreadId);

    throw new ApplicationException("Test 1");
}

[OneWay]
static void ThrowsButIsIgnored()
{
    Console.WriteLine("Thread: {0}", Thread.CurrentThread.ManagedThreadId);

    throw new ApplicationException("Test 2");
}

static void Main(string[] args)
{
    Console.WriteLine("Main: {0}", Thread.CurrentThread.ManagedThreadId);

    var willIgnoreThrow = new Action(ThrowsButIsIgnored);
    var result1 = willIgnoreThrow.BeginInvoke(null, null);

    Console.ReadLine();
    willIgnoreThrow.EndInvoke(result1);

    Console.WriteLine("============================");

    var willNotIgnoreThrow = new Action(Throws);
    var result2 = willNotIgnoreThrow.BeginInvoke(null, null);

    Console.ReadLine();
    willNotIgnoreThrow.EndInvoke(result2);
}

乔恩:谢谢你的努力,但是你没有读到这个问题。。问题是它为什么没有失败。Cheers我理解这个问题,我告诉你官方的指导是什么,并给你一些可能的原因(框架版本、配置设置),以及一些进一步的阅读来了解发生了什么:)@Bobb:你总是对那些试图免费帮助你的陌生人做出反应吗?这对你来说是怎么回事?你把“后台线程”和BackgroundWorker类混在一起了。实际上,BackgroundWorker创建后台线程。普通线程和后台线程之间的唯一区别是后台线程不会阻止您关闭应用程序,当您必须等待普通线程完成其工作时。-1:您的回答还表明,使用引发异常的方法调用
ThreadPool.QueueUserWorkItem
,将导致框架吃掉异常,这在2.0版及以后的版本中是不正确的。@João Angelo:线程池通常不会抛出异常,例如新的C#4.0任务不会抛出异常,但在tast.Wait方法中抛出异常,就像EndInvoke方法一样。同样,这是任务并行库的一个功能,而不是线程池的一个功能。尝试直接使用线程池,异常将不会被沉默。@匿名向下投票人,请解释一下为什么你认为这不能回答OP问题?我能做的最好的解释是Bobb将所有问题判断为+1或-1,并且无法理解向下投票的意义