locking声明有多昂贵?

我一直在尝试multithreading和并行处理,我需要一个计数器对处理速度进行一些基本的计数和统计分析。 为了避免同时使用我的类的问题我已经在我的类的私有variables上使用了一个locking语句:

private object mutex = new object(); public void Count(int amount) { lock(mutex) { done += amount; } } 

但我想知道…locking一个variables有多昂贵? 对性能有什么负面影响?

这是一个成本的文章 。 简短的答案是50ns。

技术上的答案是,这是不可能量化的,它很大程度上取决于CPU存储器写回缓冲器的状态以及预取器收集的数据量必须被丢弃和重新读取。 哪些都是非确定性的。 我使用150个CPU周期作为回避信号的近似值,避免了重大的失望。

实际的答案是,当你认为你可以跳过一个锁时,它比你在debugging你的代码时所花的时间要便宜。

要得到一个硬数字,你必须测量。 Visual Studio提供了一个灵活的并发分析器作为扩展。

噢亲爱的!

看来正确的答案在这里标记为答案本质上是不正确的! 我想请求作者的答复,恭敬地阅读链接文章到最后。 文章

2003年的文章的作者仅在Dual Core机器上进行了测量,在第一个测量的情况下,他仅用单线程测量了locking,每个锁的访问结果大约为50ns。

它没有提到在并发环境中的locking。 所以我们必须继续阅读这篇文章,在下半部分中,作者用两个和三个线程来测量lockingscheme,这更接近当今处理器的并发水平。

所以作者说,在Dual Core上有两个线程,这个锁的开销是120ns,而3个线程的开销是180ns。 所以它似乎明显依赖于同时访问的线程数量,更多的是更糟的。

所以很简单,它不是50纳秒,除非它是一个单一的线程,locking无用。

另一个需要考虑的问题是它是以平均时间来衡量的!

如果测量迭代的时间,则会有1ms到20ms之间的偶数次,简单,因为大多数速度很快,但是很less有线程会等待处理器时间,甚至会导致毫秒级的延迟。

对于任何需要高吞吐量,低延迟的应用来说,这都是一个坏消息。

而最后一个需要考虑的问题是锁内的操作可能会比较慢,而且往往是这样的。 代码块在锁内执行的时间越长,争用就越高,延迟时间就会越来越高。

请考虑一下,从2003年就已经过去了十几年了,那就是几代专门devise的处理器完全同时运行,locking会严重损害他们的性能。

这不会回答您关于性能的查询,但是我可以说.NET Framework确实提供了一个Interlocked.Add方法,可以让您将您的amount添加到已done成员,而无需手动locking另一个对象。

lock (Monitor.Enter / Exit)非常便宜,比Waithandle或Mutex等替代品便宜。

但是如果(稍微)慢一点,你会不会得到一个结果不正确的快速程序?

与没有锁的替代scheme相比,锁紧环的成本是巨大的。 你可以承受循环多次,仍然比锁更有效率。 这就是为什么locking空闲队列如此高效。

 using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LockPerformanceConsoleApplication { class Program { static void Main(string[] args) { var stopwatch = new Stopwatch(); const int LoopCount = (int) (100 * 1e6); int counter = 0; for (int repetition = 0; repetition < 5; repetition++) { stopwatch.Reset(); stopwatch.Start(); for (int i = 0; i < LoopCount; i++) lock (stopwatch) counter = i; stopwatch.Stop(); Console.WriteLine("With lock: {0}", stopwatch.ElapsedMilliseconds); stopwatch.Reset(); stopwatch.Start(); for (int i = 0; i < LoopCount; i++) counter = i; stopwatch.Stop(); Console.WriteLine("Without lock: {0}", stopwatch.ElapsedMilliseconds); } Console.ReadKey(); } } } 

输出:

 With lock: 2013 Without lock: 211 With lock: 2002 Without lock: 210 With lock: 1989 Without lock: 210 With lock: 1987 Without lock: 207 With lock: 1988 Without lock: 208 

有几种不同的方式来定义“成本”。 有获取和释放锁的实际开销; 正如杰克写道,这是微不足道的,除非这个操作被执行数百万次。

更相关的是这对执行stream程的影响。 此代码一次只能由一个线程input。 如果你有5个线程定期执行这个操作,其中4个将最终等待锁释放,然后在释放锁之后成为第一个计划input该代码的线程。 所以,你的algorithm会受到很大的影响。 algorithm的多less取决于操作的频率。在不引入竞争条件的情况下,你不能真正避免它,但是你可以通过减less对locking代码的调用次数来改善它。