查询静态字典会导致线程问题吗?

时间:2018-01-12 07:09:07

标签: c# .net multithreading concurrency

我有一个类(下面的简化版)

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查询,那么可能会发生类似

的事情
  • 主题1评估thingy != null && thingy.Name.Contains(query)为真的位置
  • 线程2将字典中的thingy更新为null
  • 主题1尝试在thingy.Name中使用orderby,从而导致NRE

??如果是这样,如何在不造成死锁的情况下防范?

2 个答案:

答案 0 :(得分:0)

您的具体情况不是问题。 thingy中的Find变量被分配了对各种对象的引用。在其他上下文中运行的任何内容都不能更改该变量中包含的引用,因此它永远不会从非null更改为null

答案 1 :(得分:0)

简短的回答:不,您所描述的方案是不可能的。 要了解为什么我们要深入研究 ConcurrentDictionary 实施细节。 当您调用 cache.Values 时, ConcurrentDictionary 调用 AcquireAllLocks 阻止我们修改集合(通过 cache.TryRemove ,例如)在创建值集合的过程中,创建一个 List ,将现有值放入创建的列表中(如果 TValue 是引用类型,则它仅复制引用,而不克隆对象),并且然后将创建的列表包装到 ReadOnlyCollection 中。因此,在调用 Find 方法之后,您将拥有一个基于值列表的迭代器。此列表不是 ConcurrentDictionary 的一部分,因此任何 cache.TryRemove cache.TryUpdate 都不会更改列表(并且查找方法)。