确保ASP的线程安全.网络缓存——两种策略的故事



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

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:A lock

lock (myLockObject)
{
    if(image == null){ **SNIP** }
}

选项2:分配一个浅拷贝给局部变量

var image = HttpContext.Current.Cache[cacheKey] != null ? HttpContext.Current.Cache[cacheKey].MemberwiseClone() : null;

这两个选项都有开销。第一种方法迫使线程一次一个地进入代码块。第二种方法需要在内存中创建一个新对象,该对象的大小可能非常大。

有什么其他的策略我可以在这里使用吗?

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

一般来说,您应该尝试根据应用程序逻辑提供缓存策略。

  • 检查滑动窗口缓存:而项目是旧的,当一些时间跨度-将减少不同线程的锁定-当你有大量不同的缓存项,但不确定是否会缓存
  • 考虑使用最不常用的策略:最少使用的项目当达到最大缓存大小时应该删除-最佳服务时间您有多个客户端经常访问缓存的同一部分内容。

检查一下哪个套房更适合你的BL类型并使用它。它不会完全消除锁定问题,但正确的选择将显著消除比赛条件。

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

另一个需要注意的问题是,如果在磁盘上物理更改了具有相同路径的映像的内容(不同的映像以相同的名称保存),而已经缓存了该映像,则有可能错误地提供不相关的数据。

希望有帮助。

最新更新