我目前正在使用一个包装为哈希表的哈希表。在多线程环境(如asp.net)可以使用的库中作为简单缓存同步-这适合用于此集合吗?我知道.Net 4.0中有更合适的结构,但我一直使用.Net 3.5。
如果有什么不同的话,这个缓存会经常被读取,并且很少被写入(但需要保持线程安全)。
基本用法大致如下:
Private Shared ReadOnly ExpressionCache As Hashtable = Hashtable.Synchronized(New Hashtable())
..snip...
If Not ExpressionCache.ContainsKey(myKey) Then
ExpressionCache(myKey) = myExpensiveOperationToInit()
End If
Return ExpressionCache(myKey)
..snip..
我是在做一些潜在危险的事情,还是这是一个可以接受的用例?
实际上,Hashtable
(与Dictionary<,>
不同)已经具有非常好的线程语义,可以用作缓存:它对阅读器是线程安全的(但需要锁定写入程序)-来自MSDN:
哈希表是线程安全的,可供多个读取器线程和单个写入线程使用。当只有一个线程执行写(更新)操作时,多线程使用是线程安全的,这允许无锁读取,前提是写入程序被序列化到哈希表。
(它还提到.Synchronized
支持多个作者,但坦率地说,我们自己控制它通常会得到更好的结果)
但是,为了避免幻影读取,您不应该使用单独的"contains"/"get"操作;标准用法可能是(例如使用C#):
public YourType Get(string key) {
return (YourType) expressionCache[key];
}
public void Set(string key, YourType value) {
lock(expressionCache) {
expressionCache[key] = value;
}
}
要点:
- 只有"set"有任何锁定
- "get"中只有一个操作
- 在集合中使用索引器,而不是
Add
(这样就不需要先检查"包含")
.Synchronized
包装器在大多数常见的线程场景中实际上没有什么价值。