使用ASP.Net缓存确保线程安全 - 两个策略的故事

时间:2014-06-08 18:31:18

标签: c# asp.net multithreading

我的代码目前不是线程安全的:

public byte[] GetImageByteArray(string filepath, string contentType, RImgOptions options)
{               
    //Our unique cache keys will be composed of both the image's 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;

这两个选项都有开销。第一个强制线程一次一个地进入该代码块。第二个必须在内存中创建一个可能具有非平凡大小的新对象。

我可以采用其他策略吗?

1 个答案:

答案 0 :(得分:0)

为了提供缓存解决方案的纯粹一致性,您应该在降低应用程序速度的同时锁定资源。

通常,您应该尝试根据应用程序逻辑提供缓存策略。

  • 检查滑动窗口缓存:当某些项目较旧时 时间跨度 - 将减少不同线程的锁定 - 当你 有大量不同的缓存项目,这是不确定的 再次使用。
  • 考虑使用最不常用的策略:最少使用的项目 应该在达到最大缓存大小时删除 - 最好的服务时间 你有多个客户端经常点击缓存的相同部分 内容。

只需检查哪一套更适合您的BL类型并使用它。它根本不会消除锁定问题,但正确的选择将大大消除比赛条件。

为了减少不同线程之间的共享资源,请对每个项目使用读写锁定,而不是对整个集合使用。这也会提高你的表现。

应该记住的另一个考虑因素 - 如果在已经缓存此图像的同时在磁盘上物理地更改了具有相同路径的图像的内容(不同的图像以相同的名称保存),该怎么办?错误地提供了不相关的数据。

希望它有所帮助。