Mam kilka problemów z wiodącą odpowiedzią na to pytanie.
Po pierwsze, w prawdziwej sytuacji „ odpal i zapomnij” prawdopodobnie nie wykonasz await
zadania, więc dodawanie jest bezużyteczne ConfigureAwait(false)
. Jeśli nie zwrócisz await
wartości zwróconej przez ConfigureAwait
, nie może to mieć żadnego efektu.
Po drugie, musisz być świadomy tego, co się dzieje, gdy zadanie kończy się z wyjątkiem. Rozważ proste rozwiązanie, które zasugerował @ ade-miller:
Task.Factory.StartNew(SomeMethod);
Task.Run(SomeMethod);
Wprowadza to zagrożenie: jeśli nieobsługiwany wyjątek ucieka z SomeMethod()
, ten wyjątek nigdy nie zostanie zaobserwowany i może 1 zostać ponownie zgłoszony w wątku finalizatora, powodując awarię aplikacji. Dlatego zalecałbym użycie metody pomocniczej, aby zapewnić przestrzeganie wszelkich wynikających z tego wyjątków.
Możesz napisać coś takiego:
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);
if (handler == null)
{
task.ContinueWith(
DefaultErrorContinuation,
TaskContinuationOptions.ExecuteSynchronously |
TaskContinuationOptions.OnlyOnFaulted);
}
else
{
task.ContinueWith(
t => handler(t.Exception.GetBaseException()),
TaskContinuationOptions.ExecuteSynchronously |
TaskContinuationOptions.OnlyOnFaulted);
}
}
}
Ta implementacja powinna mieć minimalne obciążenie: kontynuacja jest wywoływana tylko wtedy, gdy zadanie nie zakończy się pomyślnie, i powinna być wywoływana synchronicznie (w przeciwieństwie do planowania oddzielnie od oryginalnego zadania). W przypadku „leniwego” nie poniesiesz nawet alokacji dla delegata kontynuacji.
Rozpoczęcie operacji asynchronicznej staje się wtedy trywialne:
Blindly.Run(SomeMethod);
Blindly.Run(SomeMethod, e => Log.Warn("Whoops", e));
1. To było domyślne zachowanie w .NET 4.0. W programie .NET 4,5 domyślne zachowanie zostało zmienione w taki sposób, że niezauważone wyjątki nie będą ponownie zgłaszane w wątku finalizatora (chociaż można je nadal obserwować za pośrednictwem zdarzenia UnobservedTaskException w TaskScheduler). Jednak domyślną konfigurację można zastąpić, a nawet jeśli aplikacja wymaga .NET 4.5, nie należy zakładać, że niezauważone wyjątki zadań będą nieszkodliwe.