扭锁,有用吗?您是否经常发现自己在代码中使用自旋锁?遇到使用忙循环实际上比锁更好的用例有多常见?就个人而言,当我编写某种需要线程安全的代码时,我倾向于使用不同的同步原语对其进行基准测试,就此而言,使用锁似乎比使用自旋锁具有更好的性能。无论我实际持有锁有多少时间,使用自旋锁时收到的争用量都远远大于使用锁时收到的争用量(当然,我是在多处理器机器上运行测试)。我意识到它更有可能在“低级”代码中遇到自旋锁,但我很想知道您是否发现它对更高级的编程有用?这取决于你在做什么。在一般的应用程序代码中,您需要避免自旋锁。在低级别的东西中,您只需要锁定几条指令,并且延迟很重要,螺丝锁垫是比锁更好的解决方案。但这些情况很少见,尤其是在通常使用C#的应用程序中。在C#中,根据我的经验,“自旋锁”几乎总是比锁差——很少有自旋锁比锁表现更好的情况。然而,这并非总是如此。.NET4正在添加System.Threading.SpinLock结构。这在短时间持有锁并被反复抓取的情况下提供了好处。从关于并行编程数据结构的MSDN文档中:自旋锁提供了比其他形式的锁定更好的性能,在这种情况下,等待锁定的时间很短。在您执行锁定树等操作的情况下,自旋锁可以胜过其他锁定机制——如果您只在每个节点上锁定很短的时间,它们可以执行传统锁定。我在具有多线程场景更新的渲染引擎中遇到了这个问题——有一次——自旋锁被解析出来以使用Monitor.Enter覆盖锁。对于我的实时工作,尤其是设备驱动程序,我使用过它们。事实证明(当我最后一次计时时)像这样等待与硬件中断相关联的信号量的同步对象至少需要20微秒,无论中断实际发生多长时间。对内存映射硬件寄存器的单次检查,然后是RDTSC(允许超时以免锁定机器)处于高纳秒范围内(基本上在噪声中)。对于不应该花费太多时间的硬件级握手,很难击败自旋锁。我的2c:如果您的更新满足某些访问条件,那么它们是很好的自旋锁候选者:对于任何可能产生的东西,您应该使用通知锁结构(事件、互斥锁、信号量等)。自旋锁的一个用例是,如果您希望争用非常少,但实际上会有很多。如果您不需要支持递归锁定,则可以在单个字节中实现自旋锁,如果竞争非常低,CPU周期的浪费可以忽略不计。对于实际用例,我经常拥有包含数千个元素的数组,其中可以安全地并行更新数组的不同元素。两个线程尝试同时更新同一个元素的机会非常小(低争用),但我需要为每个元素一个锁(我会有很多)。在这些情况下,我通常分配一个与我正在并行更新的数组大小相同的ubytes数组,并实现内联自旋锁(在D编程语言中):while(!atomicCasUbyte(spinLocks[i],0,1)){}myArray[i]=newVal;atomicSetUbyte(spinLocks[i],0);另一方面,如果我必须使用常规锁,我将不得不分配一个指向对象的指针数组,然后为每个元素分配一个Mutex对象。在像上面这样的场景中,这只是一种浪费。如果您有性能关键代码并且您确定它需要比现在更快并且您确定关键因素是锁定速度,那么尝试自旋锁是个好主意。在其他情况下,何必呢?普通锁更容易正确使用。如果应该避免的话,您很少需要在应用程序代码中使用自旋锁。我不能出于任何原因在正常操作系统上运行的c#代码中使用自旋锁。繁忙的锁主要是应用程序级的浪费——自旋可以让你使用整个cpu时间片,而锁定会立即导致上下文切换(如果需要)。在某些情况下,您可能会受益于具有nr个线程=nr个处理器/核心的高性能代码,但如果您需要在该级别进行性能优化,您可能正在制作同步性较差的下一代3D游戏用于在嵌入式操作系统上工作的原语,创建操作系统/驱动程序或在任何情况下不使用c#。请注意以下几点:大多数互斥锁实现在线程实际未被调度之前会自旋一段时间。因此,很难将这些互斥锁与纯自旋锁进行比较。多个线程在同一个自旋锁上“尽可能快地”旋转会耗尽所有带宽并大大降低程序效率。您需要通过在旋转循环中添加一个noop来添加微小的“休眠”时间。我在我的HLVM项目中为垃圾收集器的世界各地阶段使用自旋锁,因为它们很简单并且是玩具VM。然而,自旋锁在这种情况下适得其反:GlasgowHaskell编译器的垃圾收集器中的一个错误非常烦人,它有一个名字,“最后的核心减速”。这是他们在GC中不恰当地使用自旋锁的直接结果,并且由于其调度程序在Linux上更加严重,但实际上只要其他程序竞争CPU时间,就会观察到这种影响。这里的效果很明显,可以看出这里的影响不仅仅是这里的最后一个核,Haskell程序的性能下降超过5个核。以上就是C#学习教程:自旋锁,有用吗?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:
