I'm working on a Web API project which uses Azure's managed cache service to cache database results in memory to improve response times and alleviate duplicate traffic to the database. When attempting to put a new item in the cache, occasionally a cache-specific exception will be thrown with a code of DataCacheErrorCode.RetryLater
. Naturally, in order to retry later without needing to block on this method I made it async
and await Task.Delay
to try again a short time later. Previously a developer had hardcoded a Thread.Sleep
in there that was really hurting application performance.
The method signature looks something similar to this now:
public static async Task Put(string cacheKey, object obj)
Following this change I get ~75 compiler warnings from all the other places in the application that called the formerly synchronous version of Put
indicating:
Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
In this case, since Put
doesn't return anything, it makes sense to me to let this operation fire-and-forget as I don't see any reason to block execution of the method that called it. I'm just wondering if there are any dangers or pitfalls for allowing a lot of these fire-and-forget Task
s running in the background as Put
can be called quite often. Or should I await anyway since 99% of the time I won't get the retry error and the Task
will finish almost immediately. I just want to make sure that I'm not incurring any penalties for having too many threads (or something like that).
If there is a chance
Put
will throw any other exception for any kind of reason, and you don't useawait Put
each time you're inserting an object to the cache, the exceptions will be swallowed inside the returnedTask
which isn't being awaited. If you're on .NET 4.0, this exception will be re-thrown inside the Finalizer of thatTask.
. If you're using .NET 4.5, it will simply be ignored (and that might not be desirable).Im just saying this to make things clear. When you use
Task.Delay
, you aren't spinning any new threads. ATask
isn't always equal to a new thread being spun. Specifically here,Task.Delay
internally uses aTimer
, so there aren't any thread overheads (except for the thread which is currently being delayed if you do useawait
).