5

Próbuję przechwycić wyjątek generowany z metody zadania przy użyciu ContinueWith i OnlyOnFaulted jak poniżej. Jednak otrzymuję nieobsługiwany wyjątek podczas próby uruchomienia tego kodu.Kontynuuj z TaskContinuationOptions.OnlyOnFaulted nie wydaje się wychwycić wyjątku zgłoszonego z uruchomionego zadania

Chciałbym, aby zadanie zostało ukończone, ponieważ już mam do czynienia z wyjątkiem. Ale Task.Wait() uruchamia się do AggregateException.

var taskAction = new Action(() => 
{ 
    Thread.Sleep(1000); 
    Console.WriteLine("Task Waited for a sec"); 
    throw (new Exception("throwing for example")); 
}); 
Task t = Task.Factory.StartNew(taskAction); 
t.ContinueWith(x => Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+ t.Exception), TaskContinuationOptions.OnlyOnFaulted); 
Console.WriteLine("Main thread waiting for 4 sec"); 
Thread.Sleep(4000); 
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?"); 
Console.WriteLine("Task State: " + t.Status); 
t.Wait();  

Jeśli poradzę sobie z wyjątkiem w metodzie zadania, jak poniżej, wszystko pójdzie normalnie, jak oczekuję. Zadanie zostanie uruchomione, wyjątek zostanie zapisany, a oczekiwanie również się powiedzie.

var taskAction = new Action(() => 
{ 
    try 
    { 
     Thread.Sleep(1000); 
     Console.WriteLine("Task Waited for a sec"); 
     throw (new Exception("throwing for example")); 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine("Catching the exception in the Action catch block only"); 
    } 
}); 
Task t = Task.Factory.StartNew(taskAction); 
t.ContinueWith(x=> Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+ t.Exception), TaskContinuationOptions.OnlyOnFaulted); 
Console.WriteLine("Main thread waiting for 4 sec"); 
Thread.Sleep(4000); 
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?"); 
Console.WriteLine("Task State: " + t.Status); 
t.Wait();  

Moje pytanie brzmi: Czy jestem używając OnlyOnFaulted prawidłowo, czy jest to zawsze lepiej obsłużyć wyjątek w metodzie zadaniowej samego? Chciałbym, aby główny wątek był kontynuowany, nawet jeśli zadanie działa w wyjątek. Ponadto chcę zalogować ten wyjątek z metody zadania.

Uwaga: Muszę zaczekać na zakończenie metody zadania przed przejściem dalej (z błędami lub bez).

Podsumowując (moje rozumienie tej pory)

Jeśli wyjątek od zadaniem jest obsługiwane to znaczy jeśli czekać albo czekają łapie wyjątek to wyjątek będzie propagowane do continuedtask onfaulted. Wyjątek może zostać przechwycony nawet w metodzie zadania i jest używany \ obsadzony.

try 
{ 
    t.wait(); 
} 
catch(Exception e) 
{ 
    LogError(e); 
} 

W powyższym przypadku, zanim LogError zostanie wywołany, wykonywane jest dalsze zadanie związane z uszkodzeniem głównego zadania.

+0

Która wersja .NET używasz? –

+0

@YuvalItzchakov przy użyciu .NET 4.5. – seesharpconcepts

Odpowiedz

8

Przede wszystkim nie używasz OnlyOnFaulted poprawnie. Kiedy używasz ContinueWith dla zadania, które naprawdę nie zmieniasz tego zadania, otrzymasz kontynuację zadania (co w twoim przypadku lekceważysz).

Jeśli oryginalna zadanie zarzucić (to był wyjątek rzucony w środku) że pozostanie mu nic zarzucić (tak nazywając Wait() na nim zawsze przekaż wyjątek). Kontynuacja przebiegałaby jednak po zarzuceniu zadania i obsłudze wyjątku.

Oznacza to, że w swoim kodzie obsługujesz wyjątek, ale ponownie go wczytujesz podając Wait(). Poprawny kod powinien wyglądać następująco:

Task originalTask = Task.Run(() => throw new Exception()); 
Task continuationTask = originalTask.ContinueWith(t => Console.WriteLine(t.Exception), TaskContinuationOptions.OnlyOnFaulted); 
continuationTask.Wait() 
// Both tasks completed. No exception rethrown 

Teraz, Yuval Itzchakov wskazał, można obsłużyć wyjątek gdziekolwiek chcesz, ale byłoby lepiej, jeśli zostały wykorzystując async-await czekać asynchronicznie, jeśli można (można” tw Main) zamiast blokowaniu Wait():

try 
{ 
    await originalTask; 
} 
catch (Exception e) 
{ 
    // handle exception 
} 
+0

Oto informacje, których szukałem.Nie mogę się upomnieć, ponieważ jestem tu nowy i nie mam wystarczającej reputacji :( – seesharpconcepts

+0

@seesharpconcepts I tak, nie możesz przegłosować odpowiedzi, dopóki nie osiągniesz 15 punktów. Więcej o tym tutaj: http: // meta .stackexchange.com/q/1661/246036 – i3arnon

-1

Wygląda na to, że jesteś na dobrej drodze. Uruchomiłem twój kod i otrzymałem te same wyniki. Moja sugestia polega na użyciu try/catch bezpośrednio z wewnątrz akcji. Spowoduje to, że Twój kod będzie bardziej przejrzysty i umożliwi rejestrowanie rzeczy, z których pochodzi, a które mogą się różnić w ścieżce kontynuacji.

var taskAction = new Action(() => 
{ 
    try{ 
     Thread.Sleep(1000); 
     Console.WriteLine("Task Waited for a sec"); 
     throw (new Exception("throwing for example")); 
    } 
    catch (Exception e) 
    { 
     Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+ e); 
    } 

}); 
Task t = Task.Factory.StartNew(taskAction); 
Console.WriteLine("Main thread waiting for 4 sec"); 
Thread.Sleep(4000); 
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?"); 
Console.WriteLine("Task State: " + t.Status); 
await t; 

[Edytuj] Usunięto zewnętrzną procedurę obsługi wyjątku.

+0

nie ma potrzeby wewnętrznego próbowania catch .... aby złapać wyjątek musisz umieścić próbkę catch gdzie dzwonisz wait lub result na zadaniu ... –

+0

Zgadzam się, ale poświęciłem czas na przeczytanie specyfikacji "Chcę zaloguj ten wyjątek z metody zadania. " –

+0

@ PhillipScottGivens Zastanawiałem się, dlaczego ContiueWith z powodu błędu nie jest wykonywane nawet w przypadku wyjątku w metodzie zadania. Przeczytałem artykuł MSDN, który sugeruje używanie ContinueWith i onlyonfaulted do obsługi wyjątków od zadań. – seesharpconcepts

3

Am I używając TaskContinutationOptions.OnlyOnFaulted prawidłowo, czy jest to zawsze lepiej obsłużyć wyjątek w metodzie zadaniowej samego? Chciałbym, aby główny wątek kontynuował, nawet jeśli zadanie uruchomi się w wyjątek.

Możesz obsługiwać wyjątki w dowolny sposób, wewnątrz lub na zewnątrz. Jest to kwestia preferencji i zazwyczaj zależy od przypadku użycia.

Należy pamiętać, że jedną z rzeczy, których nie robisz, jest odniesienie do kontynuacji. Używasz Task.Wait dla oryginalnego zadania, które propaguje wyjątek, niezależnie od tego, czy masz kontynuację, która go obsługuje.

Jedna rzecz, która mnie denerwuje to, że używasz Task.Wait który synchronicznie czeka zamiast await który asynchronicznie czeka. To jest powód AggregationException.Co więcej, nie powinieneś blokować asynchronicznych operacji, ponieważ doprowadzi cię to do rabitowej dziury, której prawdopodobnie nie chcesz, z różnego rodzaju problemami z kontekstem synchronizacji.

To, co osobiście bym zrobił, to użycie await wewnątrz ContinueWith, ponieważ jest to mniej szczegółowa opcja. Ponadto, użyję Task.Run over Task.Factory.StartNew:

var task = Task.Run(() => 
{ 
    Thread.Sleep(1000); 
    throw new InvalidOperationException(); 
} 

// Do more stuff here until you want to await the task. 

try 
{   
    await task; 
} 
catch (InvalidOperationException ioe) 
{ 
    // Log. 
} 
+0

dziękuję za odpowiedź, szczególnie w przypadku wyjątków. Nie jestem w stanie odpowiedzieć na twoją odpowiedź (być może dlatego, że jestem tu nowy i nie mam wystarczającej reputacji :() – seesharpconcepts

+0

Potrzebuję synchronicznego czekania tutaj w moim przypadku, ponieważ istnieją dwie metody, które należy ukończyć (równolegle) zanim pójdę dalej, więc myślę, że nie będzie działać w moim scenariuszu.Mogę nawet iść z Parallel.Invoke dla dwóch metod. – seesharpconcepts

+0

Dlaczego trzeba synchronicznie czekać? –

Powiązane problemy