线程安全缓存

时间:2012-10-05 21:25:00

标签: asp.net-mvc-3 caching thread-safety

我正在尝试分析我的代码中不安全线程可能遇到的问题。

在我的mvc3 webapplication中,我尝试以下内容:

// Caching code
public static class CacheExtensions
{
    public static T GetOrStore<T>(this Cache cache, string key, Func<T> generator)
    {
        var result = cache[key];
        if(result == null)
        {
          result = generator();
          lock(sync) {
              cache[key] = result;
          }
        }
    return (T)result;
    }
}

使用这样的缓存:

// Using the cached stuff
public class SectionViewData 
{
    public IEnumerable<Product> Products {get;set;}
    public IEnumerable<SomethingElse> SomethingElse {get;set;}
}

private void Testing() 
{
    var cachedSection = HttpContext.Current.Cache.GetOrStore("Some Key", 0 => GetSectionViewData());

    // Threading problem?
    foreach(var product in cachedSection.Products)
    {
         DosomestuffwithProduct...
    }
}

private SectionViewData GetSectionViewData() 
{
    SectionViewData viewData = new SectionViewData();
    viewData.Products = CreateProductList();
    viewData.SomethingElse = CreateSomethingElse();

    return viewData;
}

我可以运行IEnumerable的inte问题吗?我对线程问题没有多少经验。如果某个其他线程向缓存添加新值,则不会触及cachedSection吗?对我来说这会奏效!

我应该单独缓存Products和SomethingElse吗?这会比缓存整个SectionViewData ??

更好

2 个答案:

答案 0 :(得分:1)

线程很难;

GetOrStore方法中,get / generator序列完全不同步,因此任何nymber线程都可以从缓存中获取null并同时运行生成器函数。这可能 - 或可能不是 - 是一个问题。

您的lock语句只锁定缓存[string]的setter,它已经是线程安全的,不需要“额外锁定”。

缓存中双重检查锁定的变化是可疑的,我试图摆脱它。由于从不进入lock()部分的线程可以在没有内存屏障的情况下获得result,因此在线程获取它时可能无法完全构造result

只要没有同时修改它们,枚举缓存的IEnumrators就是安全的。如果GetSectionViewData()返回具有不可变(如不变化)集合的对象,那么您就是安全的。

答案 1 :(得分:0)

您的代码缺少部分,例如如何填充产品?仅在GetSectionViewData中? 如果是这样,那么我没有看到您的代码存在重大问题。 但是有两个线程有​​可能为同一个密钥生成相同的数据(CachedSection),它不应该创建一个线程问题,除了你正在做两次工作,所以如果这是一个昂贵的操作,我会改变代码所以它只为每个键生成一次。如果它不贵,它可以正常工作。

IEnumerable for Products没有被触及(假设您为每个线程单独创建它,但是缓存中的枚举器被修改为每个插入操作,因此它不是线程安全的。所以如果你使用它我会小心那个