Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 多线程一次写入,多次读取是否需要一个volatile?_C#_.net_Multithreading_Static_Volatile - Fatal编程技术网

C# 多线程一次写入,多次读取是否需要一个volatile?

C# 多线程一次写入,多次读取是否需要一个volatile?,c#,.net,multithreading,static,volatile,C#,.net,Multithreading,Static,Volatile,下面是一个场景。我有一个可以被多个线程(ASP.NET)访问的类,它可以从将结果存储在一次写入、多次读取的缓存中获益。此缓存对象是无法作为静态初始值设定项的一部分执行的操作的结果,但必须等待第一次执行。因此,我实现了一个简单的空检查,如下所示。我知道,如果两个线程同时执行此检查,我将计算两次开销计算,但这并不是世界末日。我的问题是,我是否需要担心由于优化或其他线程缓存,静态缓存结果仍然被其他线程视为空。一旦写入,对象就只会被读取,所以我认为不需要全刻度锁定 public class Bippi

下面是一个场景。我有一个可以被多个线程(ASP.NET)访问的类,它可以从将结果存储在一次写入、多次读取的缓存中获益。此缓存对象是无法作为静态初始值设定项的一部分执行的操作的结果,但必须等待第一次执行。因此,我实现了一个简单的空检查,如下所示。我知道,如果两个线程同时执行此检查,我将计算两次开销计算,但这并不是世界末日。我的问题是,我是否需要担心由于优化或其他线程缓存,静态缓存结果仍然被其他线程视为空。一旦写入,对象就只会被读取,所以我认为不需要全刻度锁定

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
   }

}
其他注意事项,这是在.NET4.0应用程序中

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

是的,你知道。这是
volatile
存在的主要原因之一

值得一提的是,无争用的锁增加了完全可以忽略不计的性能成本,因此没有理由只进行
lock
null检查和资源生成,因为这几乎肯定不会导致任何性能问题,并且使程序更容易推理

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

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

是的,你知道。这是
volatile
存在的主要原因之一

值得一提的是,无争用的锁增加了完全可以忽略不计的性能成本,因此没有理由只进行
lock
null检查和资源生成,因为这几乎肯定不会导致任何性能问题,并且使程序更容易推理


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

你不需要volatile,你-特别是-需要一个内存屏障以便处理器缓存同步。

你不需要volatile,你-特别是-需要一个内存屏障以便处理器缓存同步。

我认为你完全可以乐观地避免锁定,同时还要避免性能损失。可以通过两步方式测试可空性

  object readonly _cachedResultLock = new object();

  ...

  if (_cachedResult == null)
  {
     lock(_cachedResultLock)
     {
         if (_cachedResult == null)
         {
             _cachedResult = new ExpensiveCalculation(arg);
         }
     }
   }

在这里,大多数情况下,您不会访问锁,也不会序列化访问。您可以仅在第一次访问时序列化访问—但可以保证工作不会被浪费(尽管可能会导致另一个线程在第一个线程完成开销计算时等待一段时间)。

我认为您完全可以乐观地避免锁定,同时避免性能损失。可以通过两步方式测试可空性

  object readonly _cachedResultLock = new object();

  ...

  if (_cachedResult == null)
  {
     lock(_cachedResultLock)
     {
         if (_cachedResult == null)
         {
             _cachedResult = new ExpensiveCalculation(arg);
         }
     }
   }

在这里,大多数情况下,您不会访问锁,也不会序列化访问。您可以仅在第一次访问时序列化访问,但可以保证工作不会被浪费(尽管可能会导致另一个线程在第一个线程完成开销计算时等待一段时间)。

这正是
volatile
所要做的;它将确保适当的内存屏障就位…除了volatile是最差的性能方面的内存屏障。如何做到这一点?您将它与什么进行比较?我想告诉您,需要在哪里使用
线程.MemoryBarrier
。作业刚完成,还是在阅读中?@TomTom所以首先,这几乎肯定是过早的优化。在这个问题的背景下,所描述的变化几乎肯定可以忽略不计,甚至可能无法测量。这与这里描述的情况完全不同。它描述了中的一种情况,其中变量在
块内被多次访问,在块外只被访问一次。由于
已经应用了这些保证,因此在
块内部不必要地应用了内存屏障。这在这里根本没有发生,没有
;它将确保适当的内存屏障就位…除了volatile是最差的性能方面的内存屏障。如何做到这一点?您将它与什么进行比较?我想告诉您,需要在哪里使用
线程.MemoryBarrier
。作业刚完成,还是在阅读中?@TomTom所以首先,这几乎肯定是过早的优化。在这个问题的背景下,所描述的变化几乎肯定可以忽略不计,甚至可能无法测量。这与这里描述的情况完全不同。是的