当它被清除时,我应该如何使我的哈希表缓存实现线程安全?

mci*_*321 2 c# concurrency locking

我有一个用于缓存对数据库资源的访问的类.它看起来像这样:

//gets registered as a singleton
class DataCacher<T>
{
    IDictionary<string, T> items = GetDataFromDb();

    //Get is called all the time from zillions of threads
    internal T Get(string key)
    {
         return items[key];
    }

    IDictionary<string, T> GetDataFromDb() { ...expensive slow SQL access... }

    //this gets called every 5 minutes
    internal void Reset()
    {
          items.Clear();
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经稍微简化了这段代码,但它的要点是存在一个潜在的并发问题,因为当项目被清除时,如果Get被调用,事情可能会出错.

现在我可以将锁定块打包到Get和Reset中,但是我担心Get上的锁会降低站点的性能,因为Get会被Web应用程序中的每个请求线程多次调用.

我认为我可以使用双重检查锁来做一些事情,但我怀疑使用比lock {}块更聪明的东西有更简洁的方法.该怎么办?

编辑:对不起所有,我之前没有明确说明,但我使用的items.Clear()实现实际上并不是一个直字典.它是ResourceProvider的包装器,它要求字典实现在每个项目被删除时调用.ReleaseAllResources().这意味着调用代码不希望针对处置中的旧版本运行.鉴于此,Interlocked.Exchange方法是正确的吗?

Mar*_*ell 6

我将开始通过只是一个测试它lock; 在没有争议的情况下,锁很便宜.但是 - 一个更简单的方案是依赖于参考更新的原子性质:

public void Clear() {
    var tmp = GetDataFromDb(); // or new Dictionary<...> for an empty one
    items = tmp; // this is atomic; subsequent get/set will use this one
}
Run Code Online (Sandbox Code Playgroud)

您可能还想创建items一个volatile字段,以确保它不会保存在寄存器中的任何位置.

这仍然存在这样的问题:任何人都期望有一个给定的密钥可能会失望(通过例外),但这是一个单独的问题.

更精细的选项可能是a ReaderWriterLockSlim.

  • ps:我讨厌这个答案显然不为人所知,只能被大家简单地阐述.这是一个非常主流的问题,而且确实只有一个正确的答案. (3认同)