No es necesario bloquear toda la instancia de caché, sino que solo necesitamos bloquear la clave específica que está insertando. Es decir, no es necesario bloquear el acceso al baño femenino mientras usa el baño masculino :)
La siguiente implementación permite el bloqueo de claves de caché específicas mediante un diccionario concurrente. De esta manera puede ejecutar GetOrAdd () para dos claves diferentes al mismo tiempo, pero no para la misma clave al mismo tiempo.
using System;
using System.Collections.Concurrent;
using System.Web.Caching;
public static class CacheExtensions
{
private static ConcurrentDictionary<string, object> keyLocks = new ConcurrentDictionary<string, object>();
public static T GetOrAdd<T>(this Cache cache, string key, int durationInSeconds, Func<T> factory)
where T : class
{
var value = cache.Get(key);
if (value == null)
{
lock (keyLocks.GetOrAdd(key, new object()))
{
value = cache.Get(key);
if (value == null && (value = factory()) != null)
{
cache.Insert(
key: key,
value: value,
dependencies: null,
absoluteExpiration: DateTime.Now.AddSeconds(durationInSeconds),
slidingExpiration: Cache.NoSlidingExpiration,
priority: CacheItemPriority.Default,
onRemoveCallback: null);
}
keyLocks.TryRemove(key, out object locker);
}
}
return value as T;
}
}