是否值得拥有更少锁定代码但更多锁定调用?

时间:2013-10-25 20:43:17

标签: multithreading c#-4.0 thread-safety

我在访问共享变量的多线程环境中有一些代码片段。显然我需要lock才能进行这些访问,但我的问题是我是否保存通过锁定变量很短的时间来创建额外的开销一个值,解锁,用我检索的东西做一些事情,然后重新锁定以修改我刚刚解锁的数据结构。

显然,对于可以在锁之外完成的非常昂贵的操作,您可以通过不锁定该段来获益。例如,如下所示,正在进行的操作相对便宜(创建new objectif statement):

较少线程:

Value v;
lock (values)
{
   v = values.FirstOrDefault(a => a.Thing == someValue);
   if (v == null)
   {
      v = new Value { Thing = someValue };
      values.Add(v);
   }
}

更多线程:

Value v;
lock (values)
{
   v = values.FirstOrDefault(a => a.Thing == someValue);
}


if (v == null)
{
   v = new Value { Thing = someValue };
   lock(values)
   {
      values.Add(v);
   }
}

这两种解决方案都是线程安全的,并且两者都具有很强的可读性(恕我直言),但如果两者之间存在细微的差别,那么在我的习惯中建立两者的效率会更高。

2 个答案:

答案 0 :(得分:2)

第二种可能会为你带来额外的性能,但实际上,如果Value构造函数相对昂贵,那只会是一个问题。

在第二种情况下,您将避免lock超过空检查(这非常快),但也在构建Value实例时。这可能是性能方面的重大改进。

话虽如此,这是不是线程安全的。第二个线程可以将值添加到两个锁定语句之间的代码中。因此,我建议采用第一种方法(意识到你总是必须锁定values用于任何其他使用该集合的操作。)

如果此查找是常见查找,则更好的方法可能是使用ConcurrentDictionary<T,U>,而Thing作为键。然后,您可以使用GetOrAdd方法安全地添加或检索值。

答案 1 :(得分:0)

第二种情况不是线程安全的,因为当一个线程检查v是否为null时,另一个线程可以插入一个等于someValue的值(如你所知)