Thread synchronisation with SemaphoreSlim and async delegate calls

Although running a parallel foreach that makes async calls is a pretty usual thing, I found my self in a very complicated situation, when async delegate calls got in the way!

The situation arise while I was trying to write a MemoryCache manager for my application (the manager was actually a wrapper of MemoryCache Class).

The following gist file, is a basic structure of what I was trying to do: Ask MemoryCacheManager for an item with AddOrGetExisting by passing a key to find the object and a generic delegate Func task that actually returns the object.

Since this is not thread safe, my first approach was to use the lock statement, and as a countermeasure for not being able to have awaitable calls inside a lock, I played around with Task.GetAwaiter().GetResult(). Ugly :/
Sychronous block on an asycrhonous task (that could be intensive) is never a good idea.
As Stephen Cleary states in his blog:

The code … will synchronously block until the task completes. As such, it is subject to the same old deadlock problems as Wait and Result…

“GetResult” actually means “check the task for errors”…

Finally, I found a better approach after reading a few things about thread sycrhonization. I end up with the following solution that uses a semaphore slim:

* As you can see, there are two _cache.Get(key), a pattern that is called double-check locking. In short, locks are expensive and there is no reason to get one, if the requested object exists in the cache. Read more about that here and here