The*_*mon 1 c# multithreading dictionary thread-safety
我有一个复杂对象的字典,只要数据库及其源数据发生更改,就需要更新该字典。条目可能已更改、添加或删除,因此本质上我们需要重新创建/重新加载整个词典。据我所知,有几种不同的方法可以做到这一点,我想知道是否有最佳实践?(或者如果我对比较的理解是正确的)
锁定语句,逐项清除和添加
lock (myLock){
myDict.Clear();
foreach (var itemToAdd in itemsToAdd)
myDict.Add(config.key, config);
}
Run Code Online (Sandbox Code Playgroud)
逐项添加到字典中,然后删除不应该存在的内容
var tmpDict = LoadDictionary();
foreach(var key in myDict.Keys.ToList()) //check for deleted keys
if(!tmpDict.ContainsKey(key))
myDict.TryRemove(key, out _);
foreach(var pair in tmpDict) //update or add every key
myDict[pair.Key] = pair.Value;
Run Code Online (Sandbox Code Playgroud)
进行直接交换(合并将军的评论)
public ConcurrentDictionary<string, string> myDict = new ConcurrentDictionary<string, string>();;
public void UpdateDictionary(){
Interlocked.Exchange(ref myDict, LoadDictionary());
}
Run Code Online (Sandbox Code Playgroud)
据我所知,只要您从不在其他地方存储对字典的引用(因为您不会获得对引用字典的任何更新),最后一个选项就应该可以正常工作。第一个选项将停止任何访问,直到更新字典(如果担心过时的值,则更安全,并且对于较小的字典来说应该没问题),第二个选项避免锁定对象并避免丢失引用,但运行您收到更新提示后几毫秒/秒内访问旧值的风险
这是一个格式相当糟糕的问题(抱歉),但是是否有执行上述操作的最佳实践/标准?和/或上述代码片段中是否存在重大漏洞?
编辑:在我的用例中,我们有几个独立的服务正在运行,数据库将被其中一个服务更改,所以我们不能先更新字典,但有点过时也没什么大不了的。
Edit2:误解了 interlocked.exchange 的作用,但仍然改进了我的解决方案
对于选项 1,请注意,您不仅必须在写入时锁定,而且还必须在所有读取时锁定,否则锁定是无用的。这意味着读取将互相阻塞,如果走这条路——至少是用户ReaderWriterLockSlim而不是简单的锁。
选项 2 使字典在短时间内处于不一致的状态(有些值是旧的,有些是新的),这也可能是一个问题,而且ConcurrentDictionary应该比常规字典有一些开销(大多数时候它是无关紧要的,但谁知道你的具体情况)用例)。
选项 3 应该是最快的。请注意,我认为不需要使用Interlocked.Exchange,您可以只分配:
myDict = LoadDictionary();
Run Code Online (Sandbox Code Playgroud)
但还要注意,为了确保其他线程不会读取某些缓存的值 - 用 进行标记是有意义的myDict(volatile这有一些怪癖,但在您的特定用例中应该可以正常工作)。
然后,就不需要ConcurrentDictionary在选项 3 中使用,因为您永远不会写入该字典(在它被分配并可供读者使用之后)。所以最好把它做成类似的东西ReadOnlyDictionary。只要该字典从未被修改,从多个线程读取就是安全的。