如何使用两个用作接口的 Dictionary 对象来组织多线程数据存储
本文关键字:对象 Dictionary 存储 数据 多线程 接口 何使用 两个 | 更新日期: 2023-09-27 18:34:29
请向我建议一种模式来执行以下多线程任务:
我要缓存表行,需要通过两种方式找到它们:
- 按 ID (整数(
- 按键(字符串(
我希望拥有单行存储,并使用两个字典来有效地查找行。
在缓存中,我必须从数据库中读取每一行并将其存储在全局存储中,并按键和 id 将其添加到两个字典中。我需要所有这些东西必须在多线程环境中工作。
谁能建议一种优雅的方式来做到这一点?
更新。我的错。我错过了试图避免锁的明显(对我自己(的限制,因为在更"常见"用法的情况下,当从一些不同的源(不是 db(读取行时,锁可能会导致死锁......
这几乎是处理原子操作的经典案例。使用您的方法,将项目添加到缓存至少涉及三个需要原子执行的操作:从数据库中检索数据,将其存储在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];
}
}