Не потрібно блокувати весь екземпляр кеш-пам'яті, навпаки, нам потрібно лише заблокувати конкретний ключ, для якого ви вставляєте. Тобто Не потрібно блокувати доступ до жіночого туалету, поки ви користуєтеся чоловічим туалетом :)
Реалізація нижче дозволяє блокувати певні кеш-ключі за допомогою одночасного словника. Таким чином, ви можете запустити GetOrAdd () для двох різних ключів одночасно, але не для одного ключа одночасно.
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;
}
}