【问题标题】:Thread safe caching线程安全缓存
【发布时间】:2012-10-06 14:22:45
【问题描述】:

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

在我的 mvc3 网络应用程序中,我尝试以下操作:

// 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 更好吗??

【问题讨论】:

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


    【解决方案1】:

    线程很难;

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

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

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

    枚举缓存的 IEnumrator 是安全的,只要没有任何东西同时修改它们。如果GetSectionViewData() 返回一个具有不可变(如在不变的)集合中的对象,那么您是安全的。

    【讨论】:

    • 同意,因为static 并不意味着单例,尤其是在多线程场景中。
    • var cachedSection = HttpContext.Current.Cache.GetOrStore("Some Key", 0 => GetSectionViewData()); // cachedSection 是对对象的引用,对吧?那么,如果 GetSectionViewData 是静态的,这不是问题吗?
    • @NetProvoke 我并没有说GetSectionViewData() 必须是静态方法,只是它返回的内容必须是静态的(因为永远不会改变)。也许不可变是一个更好的措辞,我会改变它。
    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 2012-07-11
      • 2017-10-15
      • 2011-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-25
      • 2015-12-06
      相关资源
      最近更新 更多