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中只调用一次。但是,当我准备配置数据时,如果任何线程试图读取此数据,我将如何确保同步有效?最后,我想提高阅读操作的性能。
我能以无锁方式实现我的目标吗?
答案 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”方法中,该方法将返回您的类的实例。
这样您就不需要任何锁定机制来阅读。