【发布时间】:2021-02-18 18:19:53
【问题描述】:
我有一个复杂对象字典,只要数据库及其源数据发生更改,就需要更新该字典。条目可能已更改、添加或删除,因此基本上我们需要重新创建/重新加载整个字典。据我所知,有几种不同的方法可以做到这一点,我想知道是否有最佳实践? (或者如果我对比较的理解是正确的)
锁定语句,逐项清除和添加
lock (myLock){
myDict.Clear();
foreach (var itemToAdd in itemsToAdd)
myDict.Add(config.key, config);
}
逐项添加到字典中,然后删除不应该存在的任何内容
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;
进行直接交换(结合将军的评论)
public ConcurrentDictionary<string, string> myDict = new ConcurrentDictionary<string, string>();;
public void UpdateDictionary(){
Interlocked.Exchange(ref myDict, LoadDictionary());
}
据我所知,只要您从不在其他地方存储对字典的引用(因为您不会对引用的字典进行任何更新),最后一个选项应该可以正常工作。第一个将停止任何访问,直到字典被更新(如果过时的值是一个问题更安全,并且对于较小的字典应该没问题),第二个选项避免锁定对象并避免丢失引用,但运行你收到更新提示后访问旧值几毫秒/秒的风险
这是一个格式相当糟糕的问题(抱歉),但是否有执行上述操作的最佳实践/标准?和/或上述代码 sn-ps 中是否有任何重大漏洞?
编辑:在我的用例中,我们有几个独立的服务在运行,数据库将被其中一个更改,所以我们不能先更新字典,但有点过时并不是什么大问题要么交易。
Edit2:误解了 interlocked.exchange 的作用,但仍然改进了我的解决方案
【问题讨论】:
-
您是否关心(访问字典的人)在数据更改时是否有可能出现陈旧值?
-
如果您不关心读取过时值的机会,只需替换引用或使用
Interlocked.Exchange(ref _dict, newDictionary); -
您尚未披露与数据库数据如何变化相关的重要信息。是通过同一个应用程序吗?您可以先更新内存字典,然后启动更新数据库的任务吗?只有在确定了流程、容差和后备等情况后,我们才能得出合适的解决方案。
-
请问您为什么要这样做?如果此字典要保留某种审计功能并且您正在使用实体框架,则可以覆盖 OnSaveChanges()
-
“第二个选项 [...] 会让您面临访问旧值的风险” itemsToAdd 之后 锁定了字典,因此在此获取过程中字典是可访问的并且包含旧值。而且从数据库中获取数据应该比更新内存中的集合更耗时。
标签: c# multithreading dictionary thread-safety