更新 - 我发现lock()
吃了CPU周期的原因就像疯了一样,我在原始问题后添加了这些信息。这一切都被证明是文本的墙这样:c#锁定语句性能
TL; DR C#的内置lock()
机制,在某些情况下,使用的CPU时间不寻常的数量,如果您的系统与一个高位运行分辨率系统定时器
原题:
我有多个线程访问资源的应用程序。资源是连接到USB的设备。它是一个简单的命令/响应接口,我使用一个小的lock()
块来确保发送命令的线程也获得响应。 我的实现使用了锁(OBJ)关键词:
lock (threadLock)
{
WriteLine(commandString);
rawResponse = ReadLine();
}
当我访问此从3个线程尽可能快地(在紧凑循环)CPU使用率是高端计算机上约24%。由于USB端口的特性,每秒只能执行大约1000次命令/响应操作。 然后,我实现了这里所描述SimpleExclusiveLock锁定机构,现在的代码类似于此(一些try/catch语句东东以解除锁定在I/O异常的情况下被删除):
Lock.Enter();
WriteLine(commandString);
rawResponse = ReadLine();
Lock.Exit();
使用该实施使用相同的3个线程测试程序,CPU使用率下降到< 1%,同时仍然每秒获得1000次命令/响应操作。
问题是:在这种情况下,使用内置lock()
关键字的问题是什么?
我不小心偶然发现lock()
机制的开销非常高的情况?进入关键部分的线程只会持续约1 ms的锁定时间。
更新: lock()
像疯了似的吃CPU的原因是,一些应用程序增加了计时器的分辨率为WINMM.DLL使用timeBeginPeriod()整个系统。在我的案件的罪魁祸首是谷歌Chrome和SQL服务器 - 他们要求使用1毫秒的系统计时器分辨率:
[DllImport("winmm.dll", EntryPoint = "timeBeginPeriod", SetLastError = true)]
private static extern uint TimeBeginPeriod(uint uMilliseconds);
我发现了这一点,通过使用powercfg工具:
powercfg -energy duration 5
由于某种内置lock()
声明中的设计缺陷,这种增加的定时器分辨率会像疯狂一样吃CPU(至少在我的情况下)。 所以,我杀了那些要求高分辨率系统定时器的程序。我的应用程序现在运行速度稍慢。现在每个请求将锁定16.5 ms而不是1 ms。我猜测背后的原因是线程排定的频率较低。 CPU使用率(如任务管理器中所示)也降为零。我毫不怀疑lock()
仍然使用相当多的周期,但现在隐藏了。
在我的项目中,低CPU使用率是一个重要的设计因素。 USB请求的低1 ms延迟对整体设计也是有利的。所以(就我而言)解决方案是丢弃内置的lock()
,并用正确实施的锁定机制替换它。我已经抛出有缺陷的System.IO.Ports.SerialPort
赞成WinUSB,所以我没有担心:)
我做了一个小的控制台应用程序来演示所有这些,下午我,如果你有兴趣在副本(约100行代码)。
我想我回答我自己的问题,所以I'll刚刚离开这个位置的情况下,有人有兴趣...
在我感兴趣的是,链接的文章使用'Semaphore'而不是'lock'来等待; *应该*更昂贵(它需要去OS层等)。顺便说一句,“仅约1ms”:1ms对于计算机来说是一个非常长的时间**。 –
是的,马克,“只有1毫秒”来自线程调度器(afaik) – Mikael
Henk的短时间为1毫秒,工作量很小。但“设备”是一台复杂的机器。我的印象是,我可以创建一组线程来控制机器的各个(独立)部分,而不会有任何不必要的等待。每秒只有1000个控制I/O操作是可能的,因此大多数线程将等待获取该锁。显然,这是使用内置锁()的糟糕设计,但如果我切换到更便宜的锁机制,设计良好)它几乎就像内置锁()使用某种忙碌的等待? – Mikael