繁体   English   中英

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

[英]Why unhandled exception in a background thread doesnt crash the app domain?

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

但是,我只是尝试了下面的代码,它没有失败......任何人都可以解释为什么?

(在.NET 4和3.5中尝试过)

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

    Action a = new Action(() =>
    {
        Console.WriteLine("Background thread {0}", Thread.CurrentThread.ManagedThreadId);

        throw new ApplicationException("test exception");
    });

    a.BeginInvoke(null, null);

    Console.ReadLine();
}

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

注意:如João Angelo说,使用ThreadPool方法直接“像ThreadPool.QueueUserWorkItemsUnsafeQueueUserWorkItem ”将在2.0及以上抛出异常。

从MSDN上的托管线程中的例外

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

这是.NET Framework版本1.0和1.1的重大变化,它为许多未处理的异常提供了支持 - 例如,线程池线程中的未处理异常。 请参阅本主题后面的“从先前版本更改”。

作为临时兼容性措施,管理员可以在应用程序配置文件的部分中放置兼容性标志。 这会导致公共语言运行库恢复到版本1.0和1.1的行为。

 <legacyUnhandledExceptionPolicy enabled="1"/> 

通常使用异步委托,如果委托方法抛出异常,则线程终止,并且只有在调用EndInvoke才会在调用代码中再次抛出异常。

这就是使用异步委托( 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);
}

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

这就是backgroundWorker为你做的,如果你在BackgroundWorker的线程中有异常,它会在主线程上重新抛出。

a.BeginInvoke(null, null);

这会产生异步调用,这会创建另一个执行此操作的线程

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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