Search code examples
c#asynchronous.net-coreasync-awaitfire-and-forget

Best practices to Fire and Forget an async method to send non essential metric


Waiting for a non-essential metrics to be sent makes no sense to me, as it adds latency (waiting for the server's response) on each call to the back-end dotnet core service, which might happen multiple times per client call. Still, logging an error on failure is important (I do not need to throw it though, as the metric's failure should not impact the service).

I found multiple ways of doing so. Here is the method to be called in a FireAndForget manner:

public async Task FireAndForget()
{
    try{ await sendTheMetric(); } // Try catch on the await, to log exceptions
    catch(Exception e){ logger.debug(e) }
}

Method 1: Remove the await.

FireAndForget(); // No await in front of the call

Method 2: Seems similar to method 1 to me, as we do not await the Task.Factory.StartNew call.

Task.Factory.StartNew(async () => await MyAsyncTask());

Method 3: Enqueue on the ThreadPool as a workItem.

ThreadPool.QueueUserWorkItem(async o => await FireAndForget());

I have a hard time finding which one I should use for a Fire and forget call to send a non-essential metric. My goal is to not add latency to each calls to my service each time a metric is sent. Logging an error when the metrics fails to be sent is important, but it should never re-throw. The thread context is not important for the execution of the task. The task should always, or at least almost always, be completed.

Which one is the best practice for for requirements? Or are they all the same?

Note: I did not include async void, as it seems risky (if an exception occurs, it might crash, as no Task will wrap it).


Solution

  • Hope this doesn't muddy the water but there is a 4th option that (from what i have read) is becoming a more accepted option.

    Starting with C#7.0 you have the option of using a discard for this sort of instance.

    _ = FireAndForget();
    

    This essentially indicates that the task is not needed and thus no return value is expected. Seems to be recommended over Method 1 as it means you are explicitly indicating that it is "fire and forget" (which is less ambiguous and does not look like a coding mistake).