我正在尝试分析我的代码中不安全线程可能遇到的问题。
在我的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 ??
更好答案 0 :(得分:1)
线程很难;
在GetOrStore
方法中,get / generator序列完全不同步,因此任何nymber线程都可以从缓存中获取null并同时运行生成器函数。这可能 - 或可能不是 - 是一个问题。
您的lock
语句只锁定缓存[string]的setter,它已经是线程安全的,不需要“额外锁定”。
缓存中双重检查锁定的变化是可疑的,我试图摆脱它。由于从不进入lock()部分的线程可以在没有内存屏障的情况下获得result
,因此在线程获取它时可能无法完全构造result
。
只要没有同时修改它们,枚举缓存的IEnumrators就是安全的。如果GetSectionViewData()
返回具有不可变(如不变化)集合的对象,那么您就是安全的。
答案 1 :(得分:0)
您的代码缺少部分,例如如何填充产品?仅在GetSectionViewData中? 如果是这样,那么我没有看到您的代码存在重大问题。 但是有两个线程有可能为同一个密钥生成相同的数据(CachedSection),它不应该创建一个线程问题,除了你正在做两次工作,所以如果这是一个昂贵的操作,我会改变代码所以它只为每个键生成一次。如果它不贵,它可以正常工作。
IEnumerable for Products没有被触及(假设您为每个线程单独创建它,但是缓存中的枚举器被修改为每个插入操作,因此它不是线程安全的。所以如果你使用它我会小心那个