У меня есть код, который не является потокобезопасным:
public byte[] GetImageByteArray(string filepath, string contentType, RImgOptions options)
{
//Our unique cache keys will be composed of both the image filepath and the requested width
var cacheKey = filepath + options.Width.ToString();
var image = HttpContext.Current.Cache[cacheKey];
//If there is nothing in the cache, we need to generate the image, insert it into the cache, and return it
if (image == null)
{
RImgGenerator generator = new RImgGenerator();
byte[] bytes = generator.GenerateImage(filepath, contentType, options);
CacheItem(cacheKey, bytes);
return bytes;
}
//Image already exists in cache, serve it up!
else
{
return (byte[])image;
}
}
Мой CacheItem()
проверяет, достигнут ли максимальный размер кеша, и если он есть, он начнет удаление кэшированных элементов:
//If the cache exceeds its max allotment, we will remove items until it falls below the max
while ((int)cache[CACHE_SIZE] > RImgConfig.Settings.Profile.CacheSize * 1000 * 1000)
{
var entries = (Dictionary<string, DateTime>)cache[CACHE_ENTRIES];
var earliestCacheItem = entries.SingleOrDefault(kvp => kvp.Value == entries.Min(d => d.Value));
int length = ((byte[])cache[earliestCacheItem.Key]).Length;
cache.Remove(earliestCacheItem.Key);
cache[CACHE_SIZE] = (int)cache[CACHE_SIZE] - length;
}
Поскольку один поток может удалить элемент из кеша, поскольку другой поток ссылается на него, я могу представить два варианта:
Вариант 1: блокировка
lock (myLockObject)
{
if(image == null){ **SNIP** }
}
Вариант 2. Назначьте мелкую копию локальной переменной
var image = HttpContext.Current.Cache[cacheKey] != null ? HttpContext.Current.Cache[cacheKey].MemberwiseClone() : null;
Оба этих варианта имеют накладные расходы. Первые силы заставляют вводить этот блок кода по одному за раз. Второе требует создания нового объекта в памяти, который может иметь нетривиальный размер.
Есть ли другие стратегии, которые я мог бы использовать здесь?
Чтобы обеспечить чистую согласованность вашего решения для кеша, вы должны блокировать свой ресурс, замедляя работу приложения.
В общем, вы должны попытаться предоставить стратегию кэширования на основе вашей логики приложения.
Просто проверьте, какой из них лучше подходит вашему типу BL и используйте его. Он не удалит проблему блокировки вообще, но правильный выбор значительно устранит гоночные условия.
Чтобы уменьшить общий ресурс между различными потоками, используйте блокировки чтения и записи для каждого элемента, а не для всей коллекции. Это также повысит вашу производительность.
Еще один момент рассмотрения, который следует иметь в виду: что, если содержание изображения с одним и тем же путем физически изменяется на диске (другое изображение было сохранено с тем же именем), при том, что это изображение уже кэшировано, есть вероятность ошибочного предоставления не Соответствующие данные.
Надеюсь, это помогло.