我在访问共享变量的多线程环境中有一些代码片段。显然我需要lock
才能进行这些访问,但我的问题是我是否保存或通过锁定变量很短的时间来创建额外的开销一个值,解锁,用我检索的东西做一些事情,然后重新锁定以修改我刚刚解锁的数据结构。
显然,对于可以在锁之外完成的非常昂贵的操作,您可以通过不锁定该段来获益。例如,如下所示,正在进行的操作相对便宜(创建new object
和if 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);
}
}
这两种解决方案都是线程安全的,并且两者都具有很强的可读性(恕我直言),但如果两者之间存在细微的差别,那么在我的习惯中建立两者的效率会更高。
答案 0 :(得分:2)
第二种可能会为你带来额外的性能,但实际上,如果Value
构造函数相对昂贵,那只会是一个问题。
在第二种情况下,您将避免lock
超过空检查(这非常快),但也在构建Value
实例时。这可能是性能方面的重大改进。
话虽如此,这是不是线程安全的。第二个线程可以将值添加到两个锁定语句之间的代码中。因此,我建议采用第一种方法(意识到你总是必须锁定values
用于任何其他使用该集合的操作。)
如果此查找是常见查找,则更好的方法可能是使用ConcurrentDictionary<T,U>
,而Thing
作为键。然后,您可以使用GetOrAdd方法安全地添加或检索值。
答案 1 :(得分:0)
第二种情况不是线程安全的,因为当一个线程检查v是否为null时,另一个线程可以插入一个等于someValue的值(如你所知)