我有以下代码,其中“_ht”是表示缓存的Hashtable,而“_isLoaded”表示它是否已加载。
我们的系统有许多进程访问“_ht”对象,如果没有加载,我需要它们等待。
使用“_ht”作为锁定对象是错误的吗?我应该为此方案使用专用的对象类型类成员吗?
重要的是要提到这个班级是SINGLETON。
private Hashtable _ht = new Hashtable();
private bool _isLoaded = false;
internal Hashtable GetHT()
{
if (_isLoaded == false)
{
lock (_ht)
{
if (_isLoaded == false)
{
LoadHt(_ht);
}
}
}
return _ht;
}
答案 0 :(得分:10)
您当然可以锁定Hashtable
对象,就像您可以在lock
语句中使用.NET中的任何引用类型实例一样。但是,它通常被认为是一种较差的方法,主要是因为当一个或多个锁定对象可用于代码的其他部分时,很难跟踪代码如何使用锁定,他们也可能将其用于锁定(再次,不可思议,但你会对人们有时写的代码感到惊讶。
对于一般的锁定,最好使用单独的锁定对象。我会注意到在您的代码示例中,_ht
应该是readonly
,如果您添加一个单独的锁定对象(例如lockObj
),那也应该是只读的。
也就是说,单身场景不应该以这种方式实现。相反,您应该使用CLR自己的静态初始化,或Lazy<T>
类:
private static readonly Hashtable _ht = InitializeTable();
internal static Hashtable GetHT() { return _ht; }
private static Hashtable InitializeTable()
{
Hashtable table = new Hashtable();
LoadHt(table);
return table;
}
或者:
private static readonly Lazy<Hashtable> _ht = new Lazy<Hashtable>(() => InitializeTable());
internal static Hashtable GetHT() { return _ht.Value; }
private static Hashtable InitializeTable()
{
Hashtable table = new Hashtable();
LoadHt(table);
return table;
}
当您拥有可能被访问的类型的其他成员时,后者非常有用,但您希望确保哈希表的初始化尽可能延迟(例如,如果可能没有代码实际访问它,所以你可以避免完全初始化它。)
(我将所有内容更改为static
,因为您将方案描述为单例,在这种情况下,只有static
个成员对代码示例有意义。)
最后我会注意到Hashtable
类非常过时。作为一个非泛型类,您真的应该认真考虑升级代码以使用现在已经十年的泛型类型。 Dictionary<TKey, TValue>
类是最直接的替代,但人们有时会将Hashtable
用作简单集,HashSet<T>
数据结构更适合。
答案 1 :(得分:2)
如果您想要来到这里的第一个线程启动它就可以了。但通常你会使用锁定对象。
private Hashtable _ht = new Hashtable();
private bool _isLoaded = false;
private object lockObj = new object();
internal Hashtable GetHT()
{
if (_isLoaded == false)
{
lock (lockObj)
{
if (_isLoaded == false)
{
LoadHt(_ht);
}
}
}
return _ht;
}
答案 2 :(得分:1)
对象本身并未锁定(受保护)。 lock关键字中使用的引用用于标记或标记代码的一部分,该部分不应与使用相同对象引用的任何其他(或相同)代码段同时运行。它实际上并不影响对象本身。 所以答案是是您可以在锁定语句中使用现有的HashTable实例。
Best practice虽然是定义要锁定的私有对象,但是私有静态对象变量来保护所有实例共有的数据。
private Object thisLock = new Object();
已编辑感谢@PeterDuniho指出