如何使用两个用作接口的 Dictionary 对象来组织多线程数据存储

本文关键字:对象 Dictionary 存储 数据 多线程 接口 何使用 两个 | 更新日期: 2023-09-27 18:34:29

请向我建议一种模式来执行以下多线程任务:

我要缓存表行,需要通过两种方式找到它们:

  • 按 ID (整数(
  • 按键(字符串(

我希望拥有单行存储,并使用两个字典来有效地查找行。

在缓存中,我必须从数据库中读取每一行并将其存储在全局存储中,并按键和 id 将其添加到两个字典中。我需要所有这些东西必须在多线程环境中工作。

谁能建议一种优雅的方式来做到这一点?

更新。我的错。我错过了试图避免锁的明显(对我自己(的限制,因为在更"常见"用法的情况下,当从一些不同的源(不是 db(读取行时,锁可能会导致死锁......

如何使用两个用作接口的 Dictionary 对象来组织多线程数据存储

这几乎是处理原子操作的经典案例。使用您的方法,将项目添加到缓存至少涉及三个需要原子执行的操作:从数据库中检索数据,将其存储在dictionaryByKey中,将其存储在dictionaryByName中。

ConcurrentDictionary 在这里不会帮助你,因为该对象只能保护自己免受并发请求的影响 - 因为它不知道还有其他操作需要原子发生,它不能帮助你避免一致性问题。

基本解决方案很简单:使用 rwlock 来保护对缓存的读取和写入。ReaderWriterLock(Slim(应该可以正常工作,特别是因为我假设大多数缓存命中有望被读取。

假设MyCache是你的缓存类,获取一个项目将如下所示:

public class MyCache{
   private ReaderWriterLock rwlock;
............

......。

public object Get(int id)//same for the other one based on name
{
   rwlock.AcquireReaderLock(Timeout.Infinite);
   try{
      if(cacheID.Contains(id)){return cacheID[id];}
      //item MIGHT not be in cache (not certain since we're still under read lock)
      //1. fetch from db BEFORE upgrade to write - avoid blocking all other readers
      var item = GetItemFromStorage(id);//you get the idea
      LockCookie lk = rwlock.UpgradeToWriterLock(Timeout.Infinite);
      try{
          if(cacheID.Contains(id)){return cacheID[id];}//check again!!!
          //2. insert in cacheID
          cacheID[id]=item;
          //3. insert in cacheName
          cacheName[item->key]=item;
          //return value  
          return item;
      }finally{rwlock.DowngradeFromWriterLock(ref lk);}
   }
   finally{rwlock.ExitReadLock();}
}
private object dictLock = new object();
private Dictionary<int, int> dict1 = new Dictionary<int, int>();
private Dictionary<string, int> dict2 = new Dictionary<string, int>();
public void Add(int rownr, int id, string key)
{
    lock(dictLock)
    {
        dict1.Add(id, rownr);
        dict2.Add(key, rownr);
    }
}
public int GetRow(int id)
{
    lock(dictLock)
    {
        return dict1[id];
    }
}
public int GetRow(string key)
{
    lock(dictLock)
    {
        return dict2[key];
    }
}