2015-08-18 20 views
7

Wystąpił problem z wyłapaniem mojego wyjątku z Task.Run Zmieniłem kod i rozwiązano mój problem. Jestem gotów dowiedzieć się, jaka jest różnica między obsługą wyjątków w Task.Run na te dwa sposoby:Jak obsługiwać wyjątek Task.Run

W funkcji zewnętrznej nie mogę złapać wyjątku, ale w środku mogę go złapać.

void Outside() 
{ 
    try 
    { 
     Task.Run(() => 
     { 
      int z = 0; 
      int x = 1/z; 
     }); 
    } 
    catch (Exception exception) 
    { 
     MessageBox.Show("Outside : " + exception.Message); 
    } 
} 

void Inside() 
{ 
    Task.Run(() => 
    { 
     try 
     { 
      int z = 0; 
      int x = 1/z; 
     } 
     catch (Exception exception) 
     { 
      MessageBox.Show("Inside : "+exception.Message); 
     } 
    }); 
} 

EDIT (Duplikat Wyjaśnienie):

Nie chciałem mój Wewnątrz i na zewnątrz Funkcje być asynchroniczny ponieważ nazywając await Task.Run(...) potrzebuje funkcję uchwytu być asynchroniczny.

+1

Zobacz [Obsługa wyjątków (biblioteka zadań równoległych)] (https://msdn.microsoft.com/en-us/library/dd997415%28v=vs.110%29.aspx) –

+2

To nie jest duplikat, jeśli PO nie używa 'await' ... A jeśli używa .net 4.x, nie może użyć' czekania'. –

+0

@MatthewWatson Jesteś pewien? 'await' to .NET 4.x! –

Odpowiedz

13

Po uruchomieniu zadania wszystkie zgłaszane przez niego wyjątki są zachowywane i ponownie zgłaszane, gdy coś czeka na wynik zadania lub na wykonanie zadania.

Task.Run() zwraca Task przedmiot, którego można użyć, aby to zrobić, więc:

var task = Task.Run(...) 

try 
{ 
    task.Wait(); // Rethrows any exception(s). 
    ... 

Dla nowszych wersjach C# można użyć await zamiast ot Task.Wait():

try 
{ 
    await Task.Run(...); 
    ... 

który jest o wiele lepszy.


Dla kompletności, oto aplikacja konsoli compilable który demonstruje użycie await:

using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace ConsoleApp1 
{ 
    class Program 
    { 
     static void Main() 
     { 
      test().Wait(); 
     } 

     static async Task test() 
     { 
      try 
      { 
       await Task.Run(() => throwsExceptionAfterOneSecond()); 
      } 

      catch (Exception e) 
      { 
       Console.WriteLine(e.Message); 
      } 
     } 

     static void throwsExceptionAfterOneSecond() 
     { 
      Thread.Sleep(1000); // Sleep is for illustration only. 
      throw new InvalidOperationException("Ooops"); 
     } 
    } 
} 
+1

Również' task.Result' zgłasza wyjątek, jeśli taki istnieje. – VMAtm

+4

Nawet z nowszymi wersjami C#, nie jest * zawsze * możliwe używanie 'czekania' zamiast' Task.Wait() '. From [await (C# Reference)] (https://msdn.microsoft.com/en-us/library/hh156528%28v=vs.140%29.aspx): "Metoda asynchroniczna, w której ** czeka ** użyte musi zostać zmodyfikowane za pomocą słowa kluczowego [async] (https://msdn.microsoft.com/en-us/library/hh156513.aspx). " – DavidRR

+0

var task = Task.Run (...) i po wywołaniu metody task.Wait(), oczywiście wyjątek catched jako wszystkie operacje synchronizacji. Drugi nazywa się nigdy wersji oczekującej Task.Run (...) wywołanie async i ponownie nie można przechwycić wyjątku. lub zgubiłem ... :) –

0

Dla mnie Chciałem mój Task.Run kontynuować po wystąpieniu błędu, pozwalając kontrakt z UI błąd, ponieważ ma czas.

Moje (dziwne?) Rozwiązanie ma również mieć uruchomiony Form.Timer. My Task.Run ma swoją kolejkę (dla długich non-UI stuff), a mój Form.Timer ma swoją kolejkę (dla rzeczy UI).

Ponieważ ta metoda działała już dla mnie, dodanie obsługi błędów było banalne: jeśli task.Run dostanie błąd, dodaje informacje o błędzie do kolejki Form.Timer, która wyświetla okno dialogowe błędu.

3

Pomysł użycia Task.Wait zrobi lewę, ale spowoduje, że wywołujący wątek będzie (jak mówi kod) czekał i dlatego blokował aż do sfinalizowania zadania, co skutecznie czyni kod synchronicznym zamiast asynchronicznym.

Zamiast użyć opcji Task.ContinueWith osiągnąć wyniki:

Task.Run(() => 
{ 
    //do some work 
}).ContinueWith((t) => 
{ 
    if (t.IsFaulted) throw t.Exception; 
    if (t.IsCompleted) //optionally do some work); 
}); 

Jeśli zadanie wymaga kontynuowania wątku UI, nam możliwość TaskScheduler.FromCurrentSynchronizationContext() jako parametr na kontynuowanie tak:

).ContinueWith((t) => 
{ 
    if (t.IsFaulted) throw t.Exception; 
    if (t.IsCompleted) //optionally do some work); 
}, TaskScheduler.FromCurrentSynchronizationContext()); 

Ten kod po prostu wyrzuci zagregowany wyjątek z poziomu zadania. Oczywiście możesz też wprowadzić tutaj inną formę obsługi wyjątków.

0

W kodzie zewnętrznym sprawdzasz tylko, czy uruchomienie zadania nie spowoduje wyrzucenia wyjątku, a nie samego obiektu zadania. Działa on asynchronicznie, a kod, który go zainicjował, jest wykonywany.

Można użyć:

void Outside() 
{ 
    try 
    { 
     Task.Run(() => 
     { 
      int z = 0; 
      int x = 1/z; 
     }).GetAwaiter().GetResult(); 
    } 
    catch (Exception exception) 
    { 
     MessageBox.Show("Outside : " + exception.Message); 
    } 
} 

Korzystanie .GetAwaiter().GetResult() czeka, aż zadanie kończy i przechodzi rzucony wyjątek, ponieważ są one i nie zawinąć je w AggregateException.

Powiązane problemy