簡體   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