Eu realmente gosto desta pergunta:
Maneira mais simples de fazer um método de fogo e esquecer em C #?
Eu só quero saber que agora que temos extensões paralelas em C # 4.0, há uma maneira melhor e mais limpa de fazer Fire & Forget com Parallel linq?
Eu realmente gosto desta pergunta:
Maneira mais simples de fazer um método de fogo e esquecer em C #?
Eu só quero saber que agora que temos extensões paralelas em C # 4.0, há uma maneira melhor e mais limpa de fazer Fire & Forget com Parallel linq?
Respostas:
Não é uma resposta para 4.0, mas vale a pena notar que no .Net 4.5 você pode tornar isso ainda mais simples com:
#pragma warning disable 4014
Task.Run(() =>
{
MyFireAndForgetMethod();
}).ConfigureAwait(false);
#pragma warning restore 4014
O pragma é desativar o aviso que informa que você está executando esta tarefa como disparar e esquecer.
Se o método dentro das chaves retornar uma Tarefa:
#pragma warning disable 4014
Task.Run(async () =>
{
await MyFireAndForgetMethod();
}).ConfigureAwait(false);
#pragma warning restore 4014
Vamos decompô-lo:
Task.Run retorna uma Tarefa, que gera um aviso do compilador (aviso CS4014) observando que esse código será executado em segundo plano - é exatamente o que você queria, portanto, desabilitamos o aviso 4014.
Por padrão, as Tarefas tentam "Marshal de volta para o Thread original", o que significa que esta Tarefa será executada em segundo plano e, em seguida, tentará retornar ao Thread que a iniciou. Freqüentemente, dispare e esqueça as Tarefas terminam depois que o Thread original é concluído. Isso fará com que uma ThreadAbortException seja lançada. Na maioria dos casos, isso é inofensivo - é apenas dizer a você, eu tentei voltar, falhei, mas você não se importa de qualquer maneira. Mas ainda é um pouco barulhento ter ThreadAbortExceptions em seus logs em produção ou em seu depurador no dev local. .ConfigureAwait(false)é apenas uma maneira de ficar organizado e dizer explicitamente, execute isso em segundo plano e é isso.
Como isso é prolixo, especialmente o pragma feio, uso um método de biblioteca para isso:
public static class TaskHelper
{
/// <summary>
/// Runs a TPL Task fire-and-forget style, the right way - in the
/// background, separate from the current thread, with no risk
/// of it trying to rejoin the current thread.
/// </summary>
public static void RunBg(Func<Task> fn)
{
Task.Run(fn).ConfigureAwait(false);
}
/// <summary>
/// Runs a task fire-and-forget style and notifies the TPL that this
/// will not need a Thread to resume on for a long time, or that there
/// are multiple gaps in thread use that may be long.
/// Use for example when talking to a slow webservice.
/// </summary>
public static void RunBgLong(Func<Task> fn)
{
Task.Factory.StartNew(fn, TaskCreationOptions.LongRunning)
.ConfigureAwait(false);
}
}
Uso:
TaskHelper.RunBg(async () =>
{
await doSomethingAsync();
}
ConfigureAwait(false)em Task.Runquando você não faz awaita tarefa. O objetivo da função está em seu nome: "configure await ". Se você não fizer awaita tarefa, não registrará uma continuação e não haverá nenhum código para a tarefa "empacotar de volta ao thread original", como você diz. O maior risco é de uma exceção não observada ser relançada no encadeamento do finalizador, que esta resposta nem mesmo aborda.
#Disable Warning BC42358
Com a Taskclasse sim, mas PLINQ é realmente para consultar coleções.
Algo como o seguinte fará isso com a Tarefa.
Task.Factory.StartNew(() => FireAway());
Ou mesmo ...
Task.Factory.StartNew(FireAway);
Ou...
new Task(FireAway).Start();
Onde FireAwayestá
public static void FireAway()
{
// Blah...
}
Então, em virtude da concisão do nome da classe e do método, isso supera a versão do threadpool por entre seis e dezenove caracteres, dependendo do que você escolher :)
ThreadPool.QueueUserWorkItem(o => FireAway());
fire and forget in ASP.NET WebForms and windows.close():?
Eu tenho alguns problemas com a resposta principal a esta pergunta.
Em primeiro lugar, em uma situação verdadeira de fogo e esquecimento , você provavelmente não awaitexecutará a tarefa, por isso é inútil anexar ConfigureAwait(false). Se você não awaitdefinir o valor retornado por ConfigureAwait, ele não terá nenhum efeito.
Em segundo lugar, você precisa estar ciente do que acontece quando a tarefa é concluída com uma exceção. Considere a solução simples que @ ade-miller sugeriu:
Task.Factory.StartNew(SomeMethod); // .NET 4.0
Task.Run(SomeMethod); // .NET 4.5
Isso introduz um perigo: se uma exceção não tratada escapa SomeMethod(), nunca será observado que exceção, e podem 1 seja relançada no segmento finalizador, batendo sua aplicação. Portanto, eu recomendaria usar um método auxiliar para garantir que quaisquer exceções resultantes sejam observadas.
Você poderia escrever algo assim:
public static class Blindly
{
private static readonly Action<Task> DefaultErrorContinuation =
t =>
{
try { t.Wait(); }
catch {}
};
public static void Run(Action action, Action<Exception> handler = null)
{
if (action == null)
throw new ArgumentNullException(nameof(action));
var task = Task.Run(action); // Adapt as necessary for .NET 4.0.
if (handler == null)
{
task.ContinueWith(
DefaultErrorContinuation,
TaskContinuationOptions.ExecuteSynchronously |
TaskContinuationOptions.OnlyOnFaulted);
}
else
{
task.ContinueWith(
t => handler(t.Exception.GetBaseException()),
TaskContinuationOptions.ExecuteSynchronously |
TaskContinuationOptions.OnlyOnFaulted);
}
}
}
Essa implementação deve ter sobrecarga mínima: a continuação só é chamada se a tarefa não for concluída com êxito e deve ser chamada de forma síncrona (em vez de ser agendada separadamente da tarefa original). No caso "preguiçoso", você nem mesmo incorrerá em uma alocação para o delegado de continuação.
Iniciar uma operação assíncrona torna-se trivial:
Blindly.Run(SomeMethod); // Ignore error
Blindly.Run(SomeMethod, e => Log.Warn("Whoops", e)); // Log error
1. Esse era o comportamento padrão no .NET 4.0. No .NET 4.5, o comportamento padrão foi alterado de forma que exceções não observadas não fossem relançadas no encadeamento do finalizador (embora você ainda possa observá-las por meio do evento UnobservedTaskException em TaskScheduler). No entanto, a configuração padrão pode ser substituída e, mesmo se seu aplicativo exigir .NET 4.5, você não deve presumir que as exceções de tarefas não observadas serão inofensivas.
ContinueWithfor invocado devido ao cancelamento, a propriedade de exceção do antecedente será Null e uma exceção de referência nula será lançada. Definir a opção de continuação de tarefa como OnlyOnFaultedeliminará a necessidade de verificação de nulo ou verificará se a exceção é nula antes de usá-la.
Tasktorna-se um detalhe de implementação.
Task" não é o mesmo que "Quero uma maneira simples de iniciar uma operação em segundo plano, nunca observe seu resultado e não me importo com o mecanismo usado para fazer isso. " Com base nisso, mantenho minha resposta à pergunta que foi feita .
fire and forgetem ASP.NET WebForms e windows.close()?
Apenas para corrigir algum problema que vai acontecer com a resposta de Mike Strobel:
Se você usar var task = Task.Run(action)e depois de atribuir uma continuação a essa tarefa, correrá o risco de Tasklançar alguma exceção antes de atribuir uma continuação do manipulador de exceção ao Task. Portanto, a classe abaixo deve estar livre deste risco:
using System;
using System.Threading.Tasks;
namespace MyNameSpace
{
public sealed class AsyncManager : IAsyncManager
{
private Action<Task> DefaultExeptionHandler = t =>
{
try { t.Wait(); }
catch { /* Swallow the exception */ }
};
public Task Run(Action action, Action<Exception> exceptionHandler = null)
{
if (action == null) { throw new ArgumentNullException(nameof(action)); }
var task = new Task(action);
Action<Task> handler = exceptionHandler != null ?
new Action<Task>(t => exceptionHandler(t.Exception.GetBaseException())) :
DefaultExeptionHandler;
var continuation = task.ContinueWith(handler,
TaskContinuationOptions.ExecuteSynchronously
| TaskContinuationOptions.OnlyOnFaulted);
task.Start();
return continuation;
}
}
}
Aqui, o tasknão é executado diretamente, em vez disso, é criado, uma continuação é atribuída, e somente então a tarefa é executada para eliminar o risco de a tarefa completar a execução (ou lançar alguma exceção) antes de atribuir uma continuação.
O Runmétodo aqui retorna a continuação, Taskentão posso escrever testes de unidade, certificando-se de que a execução seja concluída. Você pode ignorá-lo com segurança em seu uso.