使用锁的正确位置在哪里

时间:2011-10-20 11:58:23

标签: c# multithreading locking thread-safety

我尝试在多线程环境中找到最佳相似性。

有没有更好的选择,或者两个版本在下面相同?

 // float bestSimilarity is shared
 // float _similarity is local

 lock(locker) 
     if (_similarity > bestSimilarity)
         bestSimilarity = _similarity;

VS

 if (_similarity > bestSimilarity)
     lock(locker) 
         bestSimilarity = _similarity;

5 个答案:

答案 0 :(得分:7)

您的第一个案例将得到保证。然而,第二种情况可能会破裂。你比较,然后请求锁定,同时另一个线程已经修改bestSimilarity而你不知道它使比较无效。

如果您想在最后一刻避开锁定,可以进行两次比较。也就是说,比较,获取一个锁,再次进行比较,只有当它仍然有效时才增加该值。请注意使用您正在比较的值的本地缓存。如果你想这样做,你需要像MemoryBarrier一样进行某种同步。这一切都变得非常复杂,所以我建议只是锁定整个事情,除非你注意到性能确实是一个瓶颈

答案 1 :(得分:2)

共享bestSimilarity时,您需要使用第一个代码段

答案 2 :(得分:1)

第二个不是线程安全的,另一个线程可以在执行if测试后更改_similarity

答案 3 :(得分:1)

第一个解决方案是线程安全的 - 第二个解决方案不是。 但是,您可以使用双重检查锁来减少获取锁的开销

if (_similarity > bestSimilarity)  
{
    lock(locker) 
    {
         if (_similarity > bestSimilarity)
             bestSimilarity = _similarity;
    }
}

答案 4 :(得分:1)

你也可以无锁:

bool retry;
do
{
    retry = false;
    var copy = Interlocked.CompareExchange(ref bestSimilarity, 0, 0);

    if (_similarity > copy)
    {
        retry = Interlocked.CompareExchange(
              ref bestSimilarity, _similarity, copy) != copy;
    }
} while (retry);

此:

  • 在开始时拍摄bestSimilarity的快照(我假设是累加器)
  • 然后将我们的当前值(_similarity与快照(稳定,作为本地)进行比较
  • 如果它更高,则交换值,但仅在累加器未更改时
  • 如果累加器已经更改,则会再次完成

这是完全线程安全的,无锁的