A (s) exceção (ões) de uma tarefa não foram observadas por Waiting on the Task ou acessando sua propriedade Exception. Como resultado, a exceção não observada foi

100

O que isso significa e como resolver?

Estou usando tarefas TPL.

Todo o erro

A (s) exceção (ões) de uma tarefa não foram observadas por Waiting on the Task ou acessando sua propriedade Exception. Como resultado, a exceção não observada foi relançada pelo encadeamento do finalizador.

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

mscorlib

MonsterMMORPG
fonte

Respostas:

158

Se você criar uma Tarefa e nunca chamar task.Wait()ou tentar recuperar o resultado de uma Task<T>, quando a tarefa for coletada pelo coletor de lixo, seu aplicativo será interrompido durante a finalização. Para obter detalhes, consulte a página do MSDN sobre Tratamento de exceções na TPL .

A melhor opção aqui é "lidar" com a exceção. Isso pode ser feito por meio de uma continuação - você pode anexar uma continuação à tarefa e registrar / engolir / etc a exceção que ocorrer. Isso fornece uma maneira limpa de registrar exceções de tarefa e pode ser escrito como um método de extensão simples, ou seja:

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);
}

Com o exposto acima, você pode evitar que qualquer tarefa destrua o aplicativo e registre-o através de:

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

Como alternativa, você pode se inscrever em TaskScheduler.UnobservedTaskException e lidar com isso lá.

Reed Copsey
fonte
17
Para maior entretenimento, tenha um método stub estático Offem uma classe chamada como a palavra de quatro letras de sua escolha e use-o para suas continuações abrangentes. Ajuda a combater parte da frustração reprimida dessa exceção em particular.
Aaronaught
1
@MonsterMMORPG Sim - você basicamente tem que capturar ou lidar com a exceção em algum lugar . Contanto que você lide com isso em algum lugar, seu problema principal irá embora.
Reed Copsey
1
Não é possível que a tarefa possa lançar uma exceção antes que a chamada para ContinueWith seja feita?
Tim Sylvester
1
@TimSylvester O framework ainda irá mapeá-lo através da continuação, mesmo se acontecer "antes" da continuação ser anexada
Reed Copsey
32
Nota importante: Isso é necessário apenas para .Net 4.0. O tratamento de exceção foi alterado por padrão .net 4.5para não destruir o aplicativo . Veja mais em Manipulação de exceções de tarefas em .NET 4.5
i3arnon
43

Certo; significa que Taskfoi finalizado após ser deixado para a coleta de lixo, mas a tarefa em si falhou. Existem duas soluções:

  • lidar com a falha de tarefas diretamente (use ContinueWith(...)para se inscrever e verificar .IsFaultede .Exceptionno Taskparâmetro)
  • manipular o TaskScheduler.UnobservedTaskExceptionevento e marcá-lo como observado (chamar e.SetObserved()após registrar o erro)
Marc Gravell
fonte
4
+1 - Com uma adição - se sua continuação não está fazendo nada além de verificar o IsFaulted, você pode usar a OnlyOnFaultedopção de continuação e evitar a verificação manual ...
Reed Copsey
na verdade, isso aconteceu onde eu chamei uma função estática pública dentro de uma tarefa tpl. usar try catch resolveria esse problema? eu realmente preciso criar outra tarefa e esperar? obrigado
MonsterMMORPG
4
1 para mencionar que SetObservedem UnobservedTaskExceptionEventArgsnecessidades a ser chamado.
James Webster de
-17

Tente este:

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
fonte
5
É um ??? O que você quer dizer? Você poderia explicar o que sua resposta contribui para as respostas até agora?
Gert Arnold
você acabou de criar uma nova tarefa continuando, que irá falhar e ficar na mesma situação.
Robert Taylor
Esta solução parece um pouco complicada. Acho que você estaria ocultando a funcionalidade sem querer, sem nenhum ganho.
Velocitas de