当我在这里填写ConcurrentDictionary时,是否需要使用lock(lockObj){}块?对于一些小背景,这将用于MVC应用程序,但我怀疑场景问题与任何多线程应用程序相关。
在搜索stackoverflow时,我没有找到这个确切的场景。第一次调用GetOptionById值请求的值时,可以通过两个单独的线程调用它。
1)将List对象值设置为锁定的私有静态是否会被认为是更好的做法,希望在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();
答案 0 :(得分:3)
你肯定得到的并不安全。考虑:
线程X和Y几乎同时调用GetOptionById
。需要填充字典的X点,并开始这样做。第一个结果返回,并添加到字典中。
Y然后发现有一个条目,并假设字典已经完成 - 所以它会获取它感兴趣的选项,这可能不是已经加载的那个。
这看起来像是使用Lazy<T>
的一个很好的候选者...你可以在那里选择适当的选项,这样一次只能有一个线程填充字典 - 第二个线程会等到第一个线程完成在继续之前。这样,“填充字典”实际上变成了原子。
如果您在第一次加载后永远不需要更新字典,您甚至可以只使用Lazy<Dictionary<string, string>>
- 只要没有编写者,就可以安全地拥有多个读者。我相信 Lazy<T>
会适当地处理内存障碍。
答案 1 :(得分:2)
您的代码中可能会出现以下问题。如果它们是可以接受的,那么你没关系,如果不是,那么你就需要使用锁。
options
null并重新创建字典。这将导致它
多次填充。 答案 2 :(得分:1)
ConcurrentDictionary
仅提供访问列表元素的线程安全性。另一方面,您的FillOptionList
方法可以从不同的线程中多次调用,所有这些方法都可以快速地将值插入到集合中。
为避免这种情况需要锁定的不是集合本身,而是GetOptionById中的条件检查。
答案 3 :(得分:1)
填写我的时候是否需要使用锁(lockObj){}块 ConcurrentDictionary在这里?
不,这个数据结构的方法已经是线程安全的。
1)创建List对象值是否被认为是更好的做法 一个私人的静态,你锁定,希望不要打电话给 在ConcurrentDictionary填充之前多次出现数据库?
也许,特别是如果GetList
本身不是线程安全的话。除了你提出的建议不起作用。 List<MyBusinessObject>
实例从GetList
返回,因此您无法锁定尚不存在的内容。相反,您将创建一个单独的对象仅用于锁定目的。
2)是否(上面的#1)甚至是必需的或者是ConcurrentDictionary 聪明到可以自己解决这个问题吗?
不,没有任何魔法会以某种方式导致GetList
连续执行。
顺便说一下,你的GetOptionById
有竞争条件。多个线程可以同时进入if
块。您的代码可能会尝试多次初始化字典。