Como lidar com todas as exceções não tratadas ao usar a Biblioteca Paralela de Tarefas?

Estou usando o TPL ( Task Parallel Library ) no .NET 4.0. Eu quero centralizar a lógica de manipulação de todas as exceções Thread.GetDomain().UnhandledException usando o Thread.GetDomain().UnhandledException . No entanto, no meu aplicativo, o evento nunca é acionado para encadeamentos iniciados com o código TPL, por exemplo, Task.Factory.StartNew(...) . O evento é de fato triggersdo se eu usar algo como new Thread(threadStart).Start() .

Este artigo do MSDN sugere o uso de Task.Wait () para capturar o AggregateException ao trabalhar com o TPL, mas isso não é o que eu quero porque esse mecanismo não é “centralizado” o suficiente.

Alguém tem o mesmo problema ou é só eu? Você tem alguma solução para isso?

Eu acho que o evento TaskScheduler.UnobservedTaskException é o que você quer:

Ocorre quando uma exceção não observada da Tarefa com falha está prestes a acionar a diretiva de escalonamento de exceção, que, por padrão, encerraria o processo.

Portanto, esse evento é semelhante ao DomainUnhandledException mencionado na sua pergunta, mas ocorre apenas para tarefas.

BTW note que a política de exceções não observadas (sim, isso não é uma exceção não observada, MS inventou novas palavras … de novo), mudou do .NET 4.0 para o .NET 4.5. No .NET 4.0 exceções não observadas leva ao encerramento do processo, mas no .NET 4.5 – não. Isso tudo é porque novas coisas assíncronas que teremos em C # 5 e VB 11.

Parece que não há uma maneira interna de lidar com isso (e nenhuma resposta a essa pergunta depois de quase duas semanas). Eu já implementei algum código personalizado para cuidar disso. A descrição da solução é bastante longa, então eu postei no meu blog. Consulte este post se você estiver interessado.

Atualização 5/7/2010: Encontrei uma maneira melhor de fazer isso, fazendo uso da continuação de tarefas. Eu crio uma class ThreadFactory que expõe o evento Error que pode ser assinado por um manipulador de nível superior e fornece methods para iniciar uma tarefa anexada com continuação adequada.
O código é postado aqui .

Atualização 4/18/2011: Post código da postagem do blog como por comentário do Nifle.

 internal class ThreadFactory { public delegate void TaskError(Task task, Exception error); public static readonly ThreadFactory Instance = new ThreadFactory(); private ThreadFactory() {} public event TaskError Error; public void InvokeError(Task task, Exception error) { TaskError handler = Error; if (handler != null) handler(task, error); } public void Start(Action action) { var task = new Task(action); Start(task); } public void Start(Action action, TaskCreationOptions options) { var task = new Task(action, options); Start(task); } private void Start(Task task) { task.ContinueWith(t => InvokeError(t, t.Exception.InnerException), TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously); task.Start(); } } 

Eu vejo duas opções que podem ser usadas para fins de centralização do tratamento de exceção no TPL: 1. Uso do evento Excepção de Tarefa Não Observada do Agendador de Tarefas. 2. Uso de continuações para tarefas com estado com falha.

Uso do evento Excepção de Tarefa Não Observada do Agendador de Tarefas.

O agendador de tarefas tem um evento UnobservedTaskException para o qual você pode se inscrever usando o operador + =.

  • Nota 1: No corpo do manipulador, você precisa chamar SetObserved () no argumento UnobservedTaskExceptionEventArgs para notificar o agendador de que a exceção foi tratada.
  • Nota 2: O manipulador é chamado quando as tarefas foram coletadas pelo coletor de lixo.
  • Nota 3: Se você esperar na tarefa, ainda será forçado a proteger a espera pelo bloco try / catch.
  • Observação 4: A diretiva padrão para exceções de tarefas não tratadas no .net 4.0 e 4.5 é diferente.

Resumo: Essa abordagem é boa para tarefas de ignorar e esquecer e para a captura de exceções escapou da política centralizada de tratamento de exceções.

Uso de continuações para tarefas com estado com falha.

Com o TPL, você pode append ações à tarefa usando o método ContinueWith (), que recebe a ação append e a opção de continuação. Esta ação será chamada após o término da tarefa e somente nos casos especificados pela opção. Em particular:

  t.ContinueWith(c => { /* exception handling code */ }, TaskContinuationOptions.OnlyOnFaulted); 

instala continuação com código de exception handling para a tarefa t. Este código será executado apenas no caso em que a Tarefa t foi finalizada devido à exceção não tratada.

  • Nota 1: Obtenha um valor de exceção no código de exception handling. Caso contrário, será borbulhado.
  • Nota 2: O código de tratamento de exceção será chamado imediatamente após o término da tarefa.
  • Nota 3: Se a exceção foi obtida no código de exception handling, ela será considerada como tratada, o bloco try / catch na tarefa em espera não será capaz de capturá-la.

Eu acho que será melhor para exception handling centralizada para usar tarefas personalizadas herdadas de tarefa com manipulador de exceção adicionado por meio de continuação. E acompanhe essa abordagem usando o evento Excepção de Tarefa Não Observada do Agendador de Tarefas para capturar tentativas de usar tarefas não personalizadas.