我有一个类(下面的简化版)
public static class ThingyLookup
{
private static ConcurrentDictionary<int, Thingy> cache;
public static UpsertThingies (IEnumerable<Thingy> thingies)
{
foreach (var thingy in thingies)
{
if (thingy.Status == ThingyStatus.Deleted)
{
Thingy removed;
cache.TryRemove(thingy.Id, out removed);
}
else
{
cache.TryUpdate(thingy.Id, thingy);
}
}
}
public static IEnumerable<Thingy> Find (string query)
{
return (from thingy in cache.Values
where thingy != null && thingy.Name.Contains(query)
orderby thingy.Name
select thingy);
}
}
如果2个线程正在运行Find
查询,那么可能会发生类似
thingy != null && thingy.Name.Contains(query)
为真的位置thingy
更新为null
thingy.Name
中使用orderby
,从而导致NRE ??如果是这样,如何在不造成死锁的情况下防范?
答案 0 :(得分:0)
您的具体情况不是问题。 thingy
中的Find
变量被分配了对各种对象的引用。在其他上下文中运行的任何内容都不能更改该变量中包含的引用,因此它永远不会从非null
更改为null
。
答案 1 :(得分:0)
简短的回答:不,您所描述的方案是不可能的。 要了解为什么我们要深入研究 ConcurrentDictionary 实施细节。 当您调用 cache.Values 时, ConcurrentDictionary 调用 AcquireAllLocks 阻止我们修改集合(通过 cache.TryRemove ,例如)在创建值集合的过程中,创建一个 List ,将现有值放入创建的列表中(如果 TValue 是引用类型,则它仅复制引用,而不克隆对象),并且然后将创建的列表包装到 ReadOnlyCollection 中。因此,在调用 Find 方法之后,您将拥有一个基于值列表的迭代器。此列表不是 ConcurrentDictionary 的一部分,因此任何 cache.TryRemove 或 cache.TryUpdate 都不会更改列表(并且查找方法)。