2011-06-24 9 views
14

Jestem całkowicie zaintrygowany. Byłem tak pewien, że .NET zamyka całą domenę aplikacji, jeśli w wątku nie ma nieprzechwyconego wyjątku, którego nigdy nie testowałem.Dlaczego nieobsługiwany wyjątek w wątku tła nie powoduje awarii domeny aplikacji?

Jednak po prostu próbowałem poniższy kod i nie zawiedzie ... Czy ktoś mógłby wyjaśnić, dlaczego?

(Próbowano w NET 4 do 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(); 
} 

Odpowiedz

9

Dzieje się tak dlatego, że BeginInvoke wykorzystuje ThreadPool wewnętrznie i kiedy ThreadPool wszelkie unhadled wyjątki będzie cisza niepowodzeniem. Jednak jeśli użyjesz a.EndInvoke, wówczas niespakowany wyjątek zostanie rzucony na metodę EndInvoke.

Uwaga: jako podano, że stosowanie metod ThreadPool bezpośrednio "jak ThreadPool.QueueUserWorkItems i UnsafeQueueUserWorkItem" spowoduje zgłaszanie wyjątków w wersji 2.0 i nowszych.

+2

-1: Twoja odpowiedź sugeruje również, że wywołanie 'ThreadPool.QueueUserWorkItem' z metodą, która zgłasza wyjątek spowoduje, że wyjątek zostanie zjedzony przez framework, co nie jest prawdą w wersji 2.0 i późniejszej. –

+0

@ João Angelo: ThreadPool w ogóle nie będzie generował wyjątków, na przykład nowe zadanie C# 4.0 nie będzie generowało wyjątków, ale w metodzie tast.Wait, podobnie jak metoda EndInvoke. –

+0

Jesteś ABSOLUTNIE w porządku. Po prostu jestem głupi! –

0

ponieważ rzut wyjątek w podanych nici pozostaje tam, chyba że jest ona przekazywana do głównego wątku.

To właśnie robimy backgroundWorker dla ciebie, jeśli masz wyjątek w wątku BackgroundWorker, zostanie on ponownie zgłoszony w głównym wątku.

a.BeginInvoke(null, null); 

to sprawia, że ​​połączenia asynchronicznego, który tworzy inny wątek, aby wykonać tę

+0

miksujesz "wątek tła" i klasę BackgroundWorker. –

+0

niezupełnie, BackgroundWorker tworzy wątek tła. Jedyną różnicą między normalnym gwintem a tylnym gwintem jest to, że tylna nitka nie zatrzymuje aplikacji od zera, podczas gdy musisz czekać na normalny wątek, aby zakończyć swoją pracę. –

6

Od Exceptions in Managed Threads na MSDN:

W wersji .NET Framework 2.0, środowisko wykonawcze wspólny język pozwala najwięcej nieobsługiwane wyjątki w wątkach do postępują naturalnie. W większości przypadków ten oznacza, że ​​nieobsługiwany wyjątek powoduje zakończenie aplikacji.

Jest to znacząca zmiana od .NET Framework 1.0 i wersji 1.1, które zapewniają sprzęgło jednokierunkowe dla wielu nieobsłużonych wyjątków - np nieobsłużone wyjątki w puli wątków wątkach. Zobacz zmiany z poprzednich wersji w dalszej części tego tematu.

Jako środek tymczasowy zgodności, administratorzy mogą umieszczać flagę kompatybilności w sekcji pliku konfiguracyjnego aplikacji . Powoduje to, że środowisko wykonawcze wspólnego języka powraca do stanu zachowań wersji 1.0 i 1.1.

<legacyUnhandledExceptionPolicy enabled="1"/> 
+0

Jon, dziękuję za trud, ale nie udało Ci się odczytać pytania. Pytanie brzmi właśnie DLACZEGO NIE DZIAŁA. Pozdrawiam –

+6

Zrozumiałem pytanie, mówię ci, jakie są oficjalne wytyczne na ten temat, i podam ci kilka możliwych powodów (wersja szkieletowa, konfiguracja) i dalsze czytanie, aby zrozumieć, co się dzieje :) –

+17

@Bobb: Czy krytyka jest taka, że ​​zawsze reagujesz na nieznajomych, którzy próbują ci pomóc za darmo? Jak to działa? –

3

Normalnie z asynchronicznych delegatów jeśli metoda delegowany zgłasza wyjątek wątek jest zakończony, a wyjątek zostanie ponownie rzucony w kodzie wywołującym tylko gdy dzwonisz EndInvoke.

Dlatego podczas korzystania z asynchronicznego uczestnika (BeginInvoke) należy zawsze dzwonić pod numer EndInvoke.Tego również nie należy mylić z Control.BeginInvoke, który można wywołać w sposób przypominający ogień i zapomnienie.

Wcześniej powiedziałem normalnie, ponieważ istnieje możliwość, aby stwierdzić, że wyjątek powinien zostać zignorowany, jeśli metoda delegata zwróci puste. Aby to zrobić, musisz oznaczyć metodę atrybutem OneWay.

Jeśli uruchomisz poniższy przykład, otrzymasz wyjątek tylko podczas wywoływania 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); 
} 
+0

@ Anonymous Downvoter, wyjaśnij, dlaczego według Ciebie to nie odpowiada na pytanie OP? –

+0

Najlepsze, że mogę wyliczyć, że Bobb ocenia wszystkie pytania jako +1 lub -1 i nie rozumie, co to jest downwinter dla –

+0

nic wspólnego ze mną. to nie jest zła odpowiedź. –

Powiązane problemy