.net Hashtable.Synchronized是否适合在多线程环境中用作简单缓存?

.net Hashtable.Synchronized是否适合在多线程环境中用作简单缓存?,.net,collections,hashtable,.net,Collections,Hashtable,我目前正在使用包装为哈希表的哈希表。同步为库中可由多线程环境(如asp.net)使用的简单缓存-这是否适合用于此集合?我知道在.NET4.0中有更多合适的结构可用,但我还是坚持使用.NET3.5 如果有什么不同的话,这个缓存经常被读取,很少被写入(但需要保持线程安全) 基本用法大致如下: Private Shared ReadOnly ExpressionCache As Hashtable = Hashtable.Synchronized(New Hashtable()) ..snip...

我目前正在使用包装为哈希表的哈希表。同步为库中可由多线程环境(如asp.net)使用的简单缓存-这是否适合用于此集合?我知道在.NET4.0中有更多合适的结构可用,但我还是坚持使用.NET3.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是线程安全的,可供多个读取器线程和单个写入线程使用。当只有一个线程执行写(更新)操作时,多线程使用它是线程安全的,这允许在将写入程序序列化到哈希表的情况下进行无锁读取

(它还提到了支持多个编写器的
.Synchronized
,但坦率地说,自己控制这一点通常会带来更好的结果)

但是,为了避免幻象读取,您不应该使用单独的“包含”/“获取”操作;标准用法可能是(例如使用C#):

要点:

  • 只有“集合”有任何锁定
  • “get”中只有一个操作
  • 在集合中使用索引器,而不是
    Add
    (这样您就不需要先选中“contains”)

.Synchronized
包装在大多数常见的线程场景中实际上没有什么价值。

事实上,
哈希表(与
字典
不同)已经具有非常好的线程语义,可以用作缓存:它对读卡器来说是线程安全的(但对写卡器来说需要锁定)——:

Hashtable是线程安全的,可供多个读取器线程和单个写入线程使用。当只有一个线程执行写(更新)操作时,多线程使用它是线程安全的,这允许在将写入程序序列化到哈希表的情况下进行无锁读取

(它还提到了支持多个编写器的
.Synchronized
,但坦率地说,自己控制这一点通常会带来更好的结果)

但是,为了避免幻象读取,您不应该使用单独的“包含”/“获取”操作;标准用法可能是(例如使用C#):

要点:

  • 只有“集合”有任何锁定
  • “get”中只有一个操作
  • 在集合中使用索引器,而不是
    Add
    (这样您就不需要先选中“contains”)

.Synchronized
包装器在大多数常见的线程场景中实际上没有什么价值;我只是想澄清一下,在多线程场景中使用.Synchronized包装器有什么问题吗?即使它不是最优化的方法?@DanP不,它也会起作用-但是:如果你小心的话,你可以不加锁地进行读取。或者可能是双重检查锁定,即
var tmp=(YourType)expressionCache[key];if(tmp==null)lock(expressionCache){tmp=(YourType)expressionCache[key];if(tmp==null){tmp=GetNewValue();expressionCache[key]=tmp;}}返回tmp
@Blam
Dictionary
不支持“7个读卡器加一个写卡器”的场景,尽管它支持“7个读卡器”或(独占)“1个写卡器”。这意味着,如果使用
字典
,则必须使用完全独占锁(例如
aka
监视器
),或者类似于
ReaderWriterLockSlim
哈希表
的无锁方法更具可伸缩性。感谢您提供了非常有用的答案;我只是想澄清一下,在多线程场景中使用.Synchronized包装器有什么问题吗?即使它不是最优化的方法?@DanP不,它也会起作用-但是:如果你小心的话,你可以不加锁地进行读取。或者可能是双重检查锁定,即
var tmp=(YourType)expressionCache[key];if(tmp==null)lock(expressionCache){tmp=(YourType)expressionCache[key];if(tmp==null){tmp=GetNewValue();expressionCache[key]=tmp;}}返回tmp
@Blam
Dictionary
不支持“7个读卡器加一个写卡器”的场景,尽管它支持“7个读卡器”或(独占)“1个写卡器”。这意味着,如果使用
字典
,则必须使用完全独占锁(例如
aka
监视器
),或者类似于
ReaderWriterLockSlim
哈希表
可能采用的无锁方法更具可伸缩性。
public YourType Get(string key) {
    return (YourType) expressionCache[key];
}
public void Set(string key, YourType value) {
    lock(expressionCache) {
        expressionCache[key] = value;
    }
}