多线程一次写入,read-many是否需要volatile?

tca*_*vin 1 .net c# static multithreading volatile

这是场景.我有一个将由多个线程(ASP.NET)访问的类,可以将结果存储在一次写入,多次读取的缓存中.此高速缓存的对象是无法作为静态初始化程序的一部分执行的操作的结果,但必须等待第一次执行.所以我实现了一个简单的null检查,如下所示.我知道如果两个线程在同一时刻点击此检查,我将计算两次ExpensiveCalculation,但这不是世界末日.我的问题是,我是否需要担心由于优化或其他线程缓存,静态_cachedResult仍然被其他线程视为null.一旦编写,该对象只被读取,所以我不认为需要全面锁定.

public class Bippi
{

   private static ExpensiveCalculation _cachedResult;

   public int DoSomething(Something arg)
   {

      // calculate only once.  recalculating is not harmful, just wastes time.
      if (_cachedResult == null);
         _cachedResult = new ExpensiveCalculation(arg);


      // additional work with both arg and the results of the precalculated
      //    values of _cachedResult.A, _cachedResult.B, and _cachedResult.C
      int someResult = _cachedResult.A + _cachedResult.B + _cachedResult.C + arg.ChangableProp;
      return someResult;

   }

}

public class ExpensiveCalculation
{

   public int A { get; private set; }
   public int B { get; private set; }
   public int C { get; private set; }

   public ExpensiveCalculation(Something arg)
   {
      // arg is used to calculate A, B, and C
   }

}
Run Code Online (Sandbox Code Playgroud)

附加说明,这是在.NET 4.0应用程序中.

Ser*_*rvy 6

我的问题是,我是否需要担心由于优化或其他线程缓存,静态_cachedResult仍然被其他线程视为null.

是的你是.这是volatile存在的主要原因之一.

值得一提的是,无争议的锁定会增加完全可忽略的性能成本,因此实际上没有理由只进行lock空检查和资源生成,因为它几乎肯定不会导致任何性能问题,并使程序更容易推理关于.

最好的解决方案是完全避免这个问题,并使用专门设计的更高级别的抽象来解决您遇到的确切问题.在这种情况下,这意味着Lazy.您可以创建一个Lazy对象来定义如何创建昂贵的资源,在需要对象的任何地方访问它,并且Lazy实现负责确保资源创建不超过一次,并且它正确地暴露给要求的代码说资源,并有效处理.