假设以下情况:
public HashTable map = new HashTable();
public void Cache(String fileName) {
if (!map.ContainsKey(fileName))
{
map.Add(fileName, new Object());
_Cache(fileName);
}
}
}
private void _Cache(String fileName) {
lock (map[fileName])
{
if (File Already Cached)
return;
else {
cache file
}
}
}
拥有以下消费者时:
Task.Run(()=> {
Cache("A");
});
Task.Run(()=> {
Cache("A");
});
是否有可能Cache
方法会抛出Duplicate key
异常,这意味着两个任务都会遇到map.add
方法并尝试添加相同的密钥?< / p>
编辑:
使用以下数据结构会解决这个并发问题吗?
public class HashMap<Key, Value>
{
private HashSet<Key> Keys = new HashSet<Key>();
private List<Value> Values = new List<Value>();
public int Count => Keys.Count;
public Boolean Add(Key key, Value value) {
int oldCount = Keys.Count;
Keys.Add(key);
if (oldCount != Keys.Count) {
Values.Add(value);
return true;
}
return false;
}
}
答案 0 :(得分:3)
是的,当然有可能。请考虑以下片段:
./build/examples/rtpose/rtpose.bin -caffemodel ./model/coco/pose_iter_440000.caffemodel -caffeproto ./model/coco/pose_deploy_linevec.prototxt -camera_resolution "40x30" -camera 0 -resolution "40x30" -start_scale 0.1 -num_scales=0 -no_display true -net_resolution "16x16"
线程1可以执行 if (!map.ContainsKey(fileName))
{
map.Add(fileName, new Object());
并发现映射不包含密钥,因此它将继续添加它,但在它有机会添加它之前,线程2也可以执行{{1}此时它还会发现地图不包含密钥,因此它也会继续添加它。当然,这将失败。
编辑(澄清后)
所以,问题似乎是如何尽可能少地锁定主if (!map.ContainsKey(fileName))
,以及如何防止缓存对象被初始化两次。
这是一个复杂的问题,所以我不能给你一个可以运行的现成的答案,(特别是因为我目前还没有方便的C#开发环境),但一般来说,我认为你应该按以下步骤操作:
使用if (!map.ContainsKey(fileName))
完全保护您的map
。
尽可能少地锁定map
;如果未找到对象在地图中,请将空对象添加到地图中并立即退出锁定。这将确保此映射不会成为进入Web服务器的所有请求的争用点。
在check-if-present-and-add-if-not片段之后,您将保留一个保证在地图中的对象。但是,此对象可能也可能不会在此时初始化。没关系。我们将在下一步处理。
重复锁定和检查的习惯用法,这次使用缓存的对象:对该特定对象感兴趣的每一个传入请求都需要锁定它,检查它是否已初始化,如果没有,则初始化它。当然,只有第一个请求才会受到初始化的惩罚。此外,在对象完全初始化之前到达的任何请求都必须等待lock()
,直到对象初始化为止。但这一切都很好,这正是你想要的。