当我在这里填写我的 ConcurrentDictionary 时是否需要使用 lock(lockObj){} block ?对于一些背景知识,这将在 MVC 应用程序中使用,但我怀疑场景问题与任何多线程应用程序相关。
在搜索 stackoverflow 时,我没有找到这个确切的场景。在第一次从 GetOptionById 值请求值时调用,它可以由两个单独的线程调用。
1) 将 List 对象的值设置为私有(private)静态值是否更好,您将其锁定以希望在填充 ConcurrentDictionary 之前不会多次调用数据库?
2) 那(上面的#1)是否必要,或者 ConcurrentDictionary 是否足够聪明,可以自己解决这个问题?在此先感谢您的任何意见。
public class MyOptions
{
static string GetOptionById(int id)
{
if (options == null || options.Count <= 0)
FillOptionList();
return options[id];
}
static void FillOptionList()
{
List<MyBusinessObject> objects = DataAccessLayer.GetList();
foreach (MyBusinessObject obj in objects)
options.TryAdd(obj.Id, obj.Name);
}
private static ConcurrentDictionary<int, string> options = new ConcurrentDictionary<int, string>();
}
编辑:感谢大家的意见,这是一种更安全的方法吗?
public static string OptionById(int id)
{
if (!options.ContainsKey(id))
{
//perhaps this is a new option and we need to reload the list
FillOptionsOrReturn(true /*force the fill*/);
return (!options.ContainsKey(id)) ? "Option not found" : options[id];
}
else
return options[id];
}
private static void FillOptionsOrReturn(bool forceFill = false)
{
List<MyBusinessClass> objectsFromDb = null;
lock (lockObj)
{
if (forceFill || options == null || options.Keys.Count <= 0)
reasons = DataAccessLayer.GetList();
}
if (objectsFromDb != null)
{
foreach (MyBusinessClass myObj in objectsFromDb)
options.TryAdd(myObj.id, myObj.name);
}
}
private static ConcurrentDictionary<int, string> options = new ConcurrentDictionary<int, string>();
private static object lockObj = new object();
最佳答案
你得到的肯定是不安全的。考虑:
线程 X 和 Y 都调用 GetOptionById
大致在同一时间。 X 发现它需要填充字典,并开始这样做。第一个结果返回,并添加到字典中。
然后 Y 发现有一个条目,并假设字典是完整的 - 所以它获取它感兴趣的选项,这可能不是已经加载的选项。 p>
这看起来很适合使用 Lazy<T>
...你可以在那里选择合适的选项,这样一次只能有一个线程填充字典——第二个线程会等到第一个线程完成后再继续。这样“填充字典”就有效地变成了原子。
如果您在第一次加载后永远不需要更新字典,您甚至可能只需要 Lazy<Dictionary<string, string>>
就可以了。 - 拥有多个读者是安全的,只要没有作者。我相信 Lazy<T>
将适本地处理内存障碍。
关于c# - 在 C# 中使用数据库值填充 ConcurrentDictionary 的锁定注意事项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9843970/