Fire and forget a Task in .NET

  • .NET

Tasks are very useful to start background work. You can use Task.Run(() => { ... }) to start a background operation. If the background operation succeeds, everything's good. If it fails, you check the task.Exception directly to know why it failed or you can await the task or use a blocking call such as Result or Wait() to get the exception. In this case you can handle the exception the way you want. If you don't watch the result of the task, the exception will eventually be raised using TaskScheduler.UnobservedTaskException. You can read more about this behavior in this post written by Stephen Toub a few years ago.

Sometimes you just want to start the task and don't wait for it nor want to know if it succeeds or not. If you just use Task.Run and the task fails, you still need to handle the exception to avoid the UnobservedTaskException event to be raised. The idea is to create an extension method to actually forget the task. There are some performance tricks explained in the comments.

public static class TaskExtensions
{
    public static void Forget(this Task task)
    {
        // note: this code is inspired by a tweet from Ben Adams. If someone find the link to the tweet I'll be pleased to add it here.
        // Only care about tasks that may fault (not completed) or are faulted,
        // so fast-path for SuccessfullyCompleted and Canceled tasks.
        if (!task.IsCompleted || task.IsFaulted)
        {
            // use "_" (Discard operation) to remove the warning IDE0058: Because this call is not awaited, execution of the current method continues before the call is completed
            // https://docs.microsoft.com/en-us/dotnet/csharp/discards#a-standalone-discard
            _ = ForgetAwaited(task);
        }

        // Allocate the async/await state machine only when needed for performance reason.
        // More info about the state machine: https://blogs.msdn.microsoft.com/seteplia/2017/11/30/dissecting-the-async-methods-in-c/
        async static Task ForgetAwaited(Task task)
        {
            try
            {
                // No need to resume on the original SynchronizationContext, so use ConfigureAwait(false)
                await task.ConfigureAwait(false);
            }
            catch
            {
                // Nothing to do here
            }
        }
    }
}

You can use this extension method like that:

Task.Run(() => { ... }).Forget();

This extension method is part of Meziantou.Framework (GitHub, NuGet). You can just add the NuGet package to your project and start using it and all the useful extension methods part of this project.

<PackageReference Include="Meziantou.Framework" Version="2.7.6" />

Do you have a question or a suggestion about this post? Contact me on Twitter or by email!

Follow me:
Enjoy this blog?Buy Me A CoffeeDonate with PayPal