Kee*_*ker 101 .net c# parallel-processing multithreading locking
我一直在尝试多线程和并行处理,我需要一个计数器来对处理速度进行一些基本的计数和统计分析.为了避免同时使用我的类的问题,我在我的类中的私有变量上使用了一个lock语句:
private object mutex = new object();
public void Count(int amount)
{
lock(mutex)
{
done += amount;
}
}
Run Code Online (Sandbox Code Playgroud)
但我想知道......锁定变量有多贵?对性能有负面影响?
Jak*_*son 74
Han*_*ant 48
技术问题是这是不可能量化的,它在很大程度上取决于CPU内存回写缓冲区的状态以及预取器收集的数据必须被丢弃和重新读取的数量.哪些都是非常不确定的.我使用150个CPU周期作为包络近似,避免了重大的失望.
实际的答案是,它是waaaay比的时候,你会燃烧在调试代码时,你认为你可以跳过锁量更便宜.
为了得到一个难以衡量的数字.Visual Studio有一个灵活的并发分析器作为扩展.
ipa*_*vlu 27
我想介绍一些我对通用同步原语感兴趣的文章,他们正在深入研究Monitor,C#锁定语句行为,属性和成本,具体取决于不同的场景和线程数.它特别感兴趣的是CPU浪费和吞吐量周期,以了解在多种情况下可以推进的工作量:
https://www.codeproject.com/Articles/1236238/Unified-Concurrency-I-Introduction https://www.codeproject.com/Articles/1237518/Unified-Concurrency-II-benchmarking-methodologies https:// www. codeproject.com/Articles/1242156/Unified-Concurrency-III-cross-benchmarking
噢亲爱的!
似乎正确的答案在这里标记为答案本质上是不正确的!我想恭敬地向作者提出答案,将链接的文章读到最后.文章
2003年文章的作者仅在双核机器上测量,在第一个测量案例中,他仅使用单个螺纹测量锁定,结果是每个锁定访问大约50ns.
它没有说明并发环境中的锁定.所以我们必须继续阅读这篇文章,在下半部分,作者正在测量具有两个和三个线程的锁定场景,这更接近当今处理器的并发级别.
所以作者说,在双核上使用两个线程,锁定成本为120ns,而使用3个线程则成为180ns.所以它似乎明显依赖于同时访问锁的线程数.
所以它很简单,它不是50 ns,除非它是单个线程,其中锁无用.
需要考虑的另一个问题是它是以平均时间来衡量的!
如果测量迭代的时间,则偶数会在1ms到20ms之间,这仅仅是因为大多数是快速的,但很少有线程会等待处理器时间并且甚至会产生毫秒长的延迟.
对于需要高吞吐量,低延迟的任何类型的应用来说,这都是坏消息.
最后一个需要考虑的问题是锁内部的操作可能会更慢,而且经常是这种情况.锁内部执行代码块的时间越长,争用越高,延迟时间越长.
请注意,从2003年开始已经过去了十多年,那几代处理器专门设计为完全同时运行并且锁定会严重损害其性能.
Ada*_*ras 20
这不会回答您关于性能的查询,但我可以说.NET Framework确实提供了一种Interlocked.Add
方法,允许您将您amount
的done
成员添加到您的成员而无需手动锁定另一个对象.
Hen*_*man 10
lock
(Monitor.Enter/Exit)非常便宜,比Waithandle或Mutex等替代品便宜.
但是,如果它(有点)缓慢,你宁愿有一个快速的程序,结果不正确吗?
与没有锁定的替代方案相比,在紧密循环中锁定的成本是巨大的.您可以承受多次循环,并且仍然比锁更有效.这就是锁定免费队列如此高效的原因.
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
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
Run Code Online (Sandbox Code Playgroud)
有几种不同的方法来定义“成本”。获取和释放锁存在实际的开销;正如杰克(Jake)所写,除非执行此操作数百万次,否则这可以忽略不计。
与此相关的是对执行流程的影响。该代码一次只能由一个线程输入。如果您有5个线程定期执行此操作,则其中4个线程最终将等待释放锁,然后成为释放该锁之后计划输入该代码段的第一个线程。因此,您的算法将遭受重大损失。这多少取决于算法以及调用该操作的频率。在不引入竞争条件的情况下,您无法真正避免它,但是可以通过减少对锁定代码的调用次数来改善它。
归档时间: |
|
查看次数: |
35256 次 |
最近记录: |