É considerado aceitável não chamar Dispose () em um object Task do TPL?

Eu quero acionar uma tarefa para executar em um segmento de plano de fundo. Eu não quero esperar pela conclusão das tarefas.

Em .net 3.5 eu teria feito isso:

ThreadPool.QueueUserWorkItem(d => { DoSomething(); }); 

Em .net 4, o TPL é o caminho sugerido. O padrão comum que tenho visto recomendado é:

 Task.Factory.StartNew(() => { DoSomething(); }); 

No entanto, o método StartNew() retorna um object Task que implementa IDisposable . Isso parece ser negligenciado por pessoas que recomendam esse padrão. A documentação do MSDN no método Task.Dispose() diz:

“Sempre chame Dispose antes de liberar sua última referência à tarefa.”

Você não pode chamar descarte em uma tarefa até que ela esteja concluída, portanto, ter o thread principal em espera e o descarte de chamadas iria, em primeiro lugar, frustrar o ponto de fazer em um thread de segundo plano. Também não parece haver nenhum evento concluído / finalizado que possa ser usado para limpeza.

A página do MSDN na class Task não faz comentários sobre isso, e o livro “Pro C # 2010 …” recomenda o mesmo padrão e não faz comentários sobre o descarte de tarefas.

Eu sei que se eu simplesmente deixar o finalizador vai pegar no final, mas isso vai voltar e me morder quando eu estiver fazendo muito fogo e esquecer tarefas como essa e o thread finalizador ficar sobrecarregado?

Então minhas perguntas são:

  • É aceitável não chamar Dispose() na class Task neste caso? E se sim, por que e existem riscos / conseqüências?
  • Existe alguma documentação que discuta isso?
  • Ou existe uma maneira apropriada de eliminar o object Task que perdi?
  • Ou existe outra maneira de fazer fogo e esquecer tarefas com o TPL?

Há uma discussão sobre isso nos fóruns do MSDN .

Stephen Toub, um membro da equipe Microsoft pfx, tem isto a dizer:

Task.Dispose existe devido a uma Tarefa potencialmente envolvendo uma manipulação de evento usada ao aguardar a conclusão da tarefa, caso o thread em espera realmente tenha que bloquear (em oposição a girar ou potencialmente executar a tarefa que está aguardando). Se tudo o que você está fazendo é usar continuações, essa alça de evento nunca será alocada

provavelmente é melhor confiar na finalização para cuidar das coisas.

Atualização (out 2012)
Stephen Toub postou um blog intitulado Preciso dispor de tarefas? que fornece mais detalhes e explica as melhorias no .Net 4.5.

Em resumo: Você não precisa descartar objects de Task 99% do tempo.

Há dois motivos principais para descartar um object: liberar resources não gerenciados de maneira pontual e determinística e evitar o custo de executar o finalizador do object. Nenhum destes se aplica a Task maioria das vezes:

  1. A partir do .net 4.5, a única vez que uma Task aloca o identificador de espera interno (o único recurso não gerenciado no object Task ) é quando você usa explicitamente o IAsyncResult.AsyncWaitHandle da Task e
  2. O object Task si não possui um finalizador; o identificador é envolvido em um object com um finalizador, portanto, a menos que seja alocado, não há finalizador para executar.

Esse é o mesmo tipo de problema da class Thread. Ele consome 5 alças do sistema operacional, mas não implementa IDisposable. Boa decisão dos designers originais, há, naturalmente, algumas maneiras razoáveis ​​de chamar o método Dispose (). Você teria que chamar Join () primeiro.

A class Task adiciona um identificador a isso, um evento interno de redefinição manual. Qual é o recurso de sistema operacional mais barato que existe. Naturalmente, seu método Dispose () só pode liberar aquele identificador de evento, não os 5 manipuladores que Thread consome. Sim, não se incomode .

Cuidado com o que você deveria estar interessado na propriedade IsFaulted da tarefa. É um assunto bastante feio, você pode ler mais sobre isso neste artigo da MSDN Library . Depois de lidar com isso corretamente, você também deve ter um bom lugar no código para descartar as tarefas.

Eu adoraria ver alguém pesando sobre a técnica mostrada neste post: Invocação de delegado assíncrona de ignorar e esquecer do Typesafe em C #

Parece que um método de extensão simples lidará com todos os casos triviais de interação com as tarefas e poderá chamar de descarte.

 public static void FireAndForget(this Action act,T arg1) { var tsk = Task.Factory.StartNew( ()=> act(arg1), TaskCreationOptions.LongRunning); tsk.ContinueWith(cnt => cnt.Dispose()); }