有没有办法实现无锁静态配置数据?

时间:2012-09-11 06:52:28

标签: c# lock-free static-content

public class MyConfigurationData
{
   public double[] Data1 { get; set; }
   public double[] Data2 { get; set; }
}
public class MyClass
{
   private static object SyncObject = new object();
   private static MyConfigurationData = null;
   private static MyClass()
   {
      lock(SyncObject)
      {
         //Initialize Configuration Data
         //This operation is bit slow as it needs to query the DB to retreive configuration data
      }
   }
   public static MyMethodWhichNeedsConfigurationData()
   {
      lock(SyncObject)
      {
          //Multilple threads can call this method
          //I lock only to an extent where I attempt to read the configuration data
      }
   }
}

在我的应用程序中,我只需要创建一次配置数据并多次使用它。换句话说,我写了一次并多次阅读。而且,我想确保在写操作完成之前不应该进行读取。换句话说,我不想将MyConfigurationData读为NULL。

我所知道的是静态构造函数在AppDomain中只调用一次。但是,当我准备配置数据时,如果任何线程试图读取此数据,我将如何确保同步有效?最后,我想提高阅读操作的性能。

我能以无锁方式实现我的目标吗?

3 个答案:

答案 0 :(得分:4)

来自MSDN

  

静态构造函数用于初始化任何静态数据,或执行仅需执行一次的特定操作。在创建第一个实例或引用任何静态成员之前会自动调用它。

因此,您不需要在代码中使用lock,它实际上是线程安全的。在引用MyMethodWhichNeedsConfigurationData之前调用静态构造函数。

public class MyClass
{
   private static MyConfigurationData = null;
   private static MyClass()
   {
   }

   public static MyMethodWhichNeedsConfigurationData()
   {
   }
}

答案 1 :(得分:2)

只要您只是读取数据,它应该已经是线程安全的。当只读时,很少有数据结构不是线程安全的(明显的反例可能包括延迟加载)。请注意,静态构造函数由运行时自动同步,因此您无需关心运行“初始化配置数据”步骤的多个线程。

所以:只要没有任何东西可以改变数据,你就已经安全了。你也可以通过将数据隐藏在不可变接口后面来使错误变得更难,即

public class ConfigurationData {
    // or some similar immutable API...
    public double GetData1(int index) { return data1[index]; }
    public double GetData2(int index) { return data2[index]; }

    private readonly double[] data1, data2;

    public ConfigurationData(double[] data1, double[] data2) {
        this.data1 = data1;
        this.data2 = data2;
    }
}

然后你不需要任何锁:

public class MyClass
{
   private static MyConfigurationData;
   private static MyClass()
   {
     //Initialize Configuration Data
       MyConfigurationData = ...
     //This operation is bit slow as it needs to query the DB to retreive configuration data
   }
   public static MyMethodWhichNeedsConfigurationData()
   {          //Multilple threads can call this method
      var config = MyConfigurationData;

   }
}

请注意,删除锁可以改善 parallelism ;它不会改变原始的单线程性能。

那说:我应该建议反对静态数据 ;这使得测试变得非常困难,如果您的需求发生变化,那么做多租户就会变得很棘手。 拥有单个配置实例可能更为谨慎,但是将其作为某种形式的 context 传递给系统。但是,这两种方法都可以成功使用 - 这只是需要注意的事项。

答案 2 :(得分:1)

我认为您应该使用Singleton模式并将配置初始化逻辑放在“GetInstance”方法中,该方法将返回您的类的实例。

这样您就不需要任何锁定机制来阅读。