Wyjątki zadania nie zostały zaobserwowane ani przez oczekiwanie na zadanie, ani przez dostęp do jego właściwości Exception. W rezultacie niezauważonym wyjątkiem był

100

Co to oznacza i jak go rozwiązać?

Używam zadań TPL.

Cały błąd

Wyjątki zadania nie zostały zaobserwowane ani przez oczekiwanie na zadanie, ani przez dostęp do jego właściwości Exception. W rezultacie niezauważony wyjątek został ponownie zgłoszony przez wątek finalizatora.

w System.Threading.Tasks.TaskExceptionHolder.Finalize ()

mscorlib

MonsterMMORPG
źródło

Odpowiedzi:

158

Jeśli utworzysz zadanie i nigdy nie zadzwonisz task.Wait()ani nie spróbujesz pobrać wyniku a Task<T>, gdy zadanie jest zbierane przez moduł odśmiecania pamięci, zburzy to aplikację podczas finalizacji. Aby uzyskać szczegółowe informacje, zobacz stronę MSDN dotyczącą obsługi wyjątków w TPL .

Najlepszą opcją jest „obsłużenie” wyjątku. Można to zrobić poprzez kontynuację - możesz dołączyć kontynuację do zadania i zarejestrować / połknąć / itp. Wyjątek, który wystąpił. Zapewnia to czysty sposób rejestrowania wyjątków zadań i można go zapisać jako prostą metodę rozszerzenia, tj .:

public static void LogExceptions(this Task task)
{
    task.ContinueWith( t =>
    {
         var aggException = t.Exception.Flatten();
         foreach(var exception in aggException.InnerExceptions)
             LogException(exception);
    }, 
    TaskContinuationOptions.OnlyOnFaulted);
}

W ten sposób możesz zapobiec zniszczeniu aplikacji i zarejestrowaniu jej przez jakiekolwiek zadanie poprzez:

Task.Factory.StartNew( () => 
   { 
       // Do your work...
   }).LogExceptions();

Alternatywnie możesz zasubskrybować TaskScheduler.UnobservedTaskException i tam go obsłużyć.

Reed Copsey
źródło
17
Aby uzyskać dodatkową rozrywkę, Offużyj statycznej metody pośredniczącej w klasie nazwanej jako wybrane czteroliterowe słowo i używaj jej do kontynuacji typu catch-all. Pomaga zwalczyć stłumioną frustrację wynikającą z tego szczególnego wyjątku.
Aaronaught
1
@MonsterMMORPG Tak - po prostu musisz gdzieś złapać lub obsłużyć wyjątek . Tak długo, jak gdzieś sobie z tym poradzisz, twój podstawowy problem zniknie.
Reed Copsey,
1
Czy nie jest możliwe, że zadanie może zgłosić wyjątek przed wywołaniem ContinueWith?
Tim Sylvester
1
@TimSylvester Framework nadal będzie go mapował poprzez kontynuację, nawet jeśli zdarzy się to „przed” dołączeniem kontynuacji
Reed Copsey,
32
Ważna uwaga: jest to konieczne tylko w przypadku .Net 4.0. Obsługa wyjątków została domyślnie zmieniona, .net 4.5aby nie zrywać aplikacji . Zobacz więcej w sekcji Obsługa wyjątków zadań w .NET 4.5
i3arnon
43

Pewnie; oznacza to, że Taskzostało sfinalizowane po pozostawieniu go do czyszczenia pamięci, ale samo zadanie nie powiodło się. Istnieją dwie poprawki:

  • obsłużyć zadania nie powiodło się bezpośrednio (użyj ContinueWith(...)do subskrybowania i sprawdź .IsFaultedi .Exceptionna Taskparametrze)
  • obsłużyć TaskScheduler.UnobservedTaskExceptionzdarzenie i oznaczyć je jako obserwowane (wywołanie e.SetObserved()po zarejestrowaniu błędu)
Marc Gravell
źródło
4
+1 - Z jednym dodatkiem - jeśli Twoja kontynuacja nie robi nic poza sprawdzeniem IsFaulted, możesz użyć OnlyOnFaultedopcji kontynuacji i uniknąć ręcznego sprawdzania ...
Reed Copsey
faktycznie stało się tak, gdy wywołałem publiczną funkcję statyczną wewnątrz zadania tpl. użycie try catch rozwiązałoby ten problem? czy naprawdę muszę tworzyć kolejne zadanie i czekać? dzięki
MonsterMMORPG
4
+1 dla wspomnieć, że SetObservedna UnobservedTaskExceptionEventArgspotrzeby nazywać.
James Webster,
-17

Spróbuj tego:

public static void ThrowFirstExceptionIfHappens(this Task task)
{
    task.ContinueWith(t =>
    {
        var aggException = t.Exception.Flatten();
        foreach (var exception in aggException.InnerExceptions)
        {
            throw exception; // throw only first, search for solution
        }
    },
    TaskContinuationOptions.OnlyOnFaulted); // not valid for multi task continuations
}

public static Task CreateHandledTask(Action action) 
{
    Task tsk = Task.Factory.StartNew(action);
    tsk.ThrowFirstExceptionIfHappens();
    return tsk;
}
h4165f8ghd4f854d6f8h
źródło
5
To jeden ??? Co masz na myśli? Czy mógłbyś wyjaśnić, jak Twoja odpowiedź wpłynęła na dotychczasowe odpowiedzi?
Gert Arnold
właśnie utworzyłeś nowe zadanie, kontynuując, które zakończy się niepowodzeniem i znajdzie się w tej samej sytuacji.
Robert Taylor
To rozwiązanie wydaje się nieco zawiłe. Myślę, że nieumyślnie ukrywałbyś funkcjonalność bez żadnego zysku.
Velocitas