在程序集中实现自旋锁。在这里,我发布了一个我想出的解决方案。这是对的吗?你知道一个较短的吗?
锁:
mov ecx, 0
.loop:
xchg [eax], ecx
cmp ecx, 0
je .loop
释放:
lock dec dword [eax]
eax初始化为-1(表示锁定是免费的)。这适用于许多线程(不一定是2)。
答案 0 :(得分:5)
最短可能是:
acquire:
lock bts [eax],0
jc acquire
release:
mov [eax],0
为了提高性能,最好使用“测试,测试和设置”方法,并使用pause
,如下所示:
acquire:
lock bts [eax],0 ;Optimistic first attempt
jnc l2 ;Success if acquired
l1:
pause
test [eax],1
jne l1 ;Don't attempt again unless there's a chance
lock bts [eax],0 ;Attempt to acquire
jc l1 ;Wait again if failed
l2:
release:
mov [eax],0
对于调试,您可以添加额外的数据,以便更容易地检测到问题,例如:
acquire:
lock bts [eax],31 ;Optimistic first attempt
jnc l2 ;Success if acquired
mov ebx,[CPUnumber]
lea ebx,[ebx+0x80000000]
cmp [eax],ebx ;Is the lock acquired by this CPU?
je .bad ; yes, deadlock
lock inc dword [eax+4] ;Increase "lock contention counter"
l1:
pause
test [eax],0x80000000
jne l1 ;Don't attempt again unless there's a chance
lock bts [eax],31 ;Attempt to acquire
jc l1 ;Wait again if failed
l2: mov [eax],ebx ;Store CPU number
release:
mov ebx,[CPUnumber]
lea ebx,[ebx+0x80000000]
cmp [eax],ebx ;Is lock acquired, and is CPU same?
jne .bad ; no, either not acquired or wrong CPU
mov [eax],0
答案 1 :(得分:1)
您的代码很好,但如果您正在寻找高性能,我建议您这样做:
xor ecx, ecx
.loop:
lock xchg [eax], ecx
test ecx, ecx
jz .loop
原因:
xor ecx, ecx
较小,不需要文字,而现代CPU有硬连线快速注册零。test ecx, ecx
可能比cmp ecx, 0
略小且速度快,因为您不需要加载文字,而test
只是一个按位AND操作而不是减法。P.S。我总是把锁前缀放在那里,不管是否隐含,出于可读性的原因 - 这显然表明我正在进行锁定操作。
答案 2 :(得分:-1)
您的代码很好,如果您有空间问题,可以随时尝试缩短代码。
其他答案提到了性能,并且显示了对锁如何工作的基本无知。
当尝试锁定时,有问题的核心会在其中一个引脚(LOCK)上发出一个信号,告知所有其他内核,它们的缓存,所有内存和所有总线主控设备(因为它们可以独立于核心)完成任何未完成的内存操作。当他们这样做时,他们共同提出另一个信号 - 锁定确认(LOCKA) - 它返回到原始核心并进行存储器交换。此后,LOCK信号关闭。
到达此处后,您可以查看使用xchg获取的值。如果事实证明另一个任务/线程拥有锁,则需要重新执行锁定序列。
假设计算机上任何位置最慢的总线控制设备都是33MHz PCI卡。如果它正在做某事,则需要任意数量的PCI总线时钟周期才能完成。每个周期都意味着3.3GHz CPU上有一百个等待周期。将此视为在锁定序列中保存一个或两个循环的角度。 CPU,芯片组和主板中有几条总线,有些,全部或全部都不能在任何时候激活 - 例如启动LOCK时。使用LOCKA回复最长的主动总线将决定锁完成的速度。
亲自尝试:衡量一千万个自旋锁(抓取和释放)需要多长时间。
总线锁(自旋锁,Windows中的关键部分)的性能技巧是尽可能少地使用它们,这意味着组织数据以实现这一点。总线锁可能在非服务器计算机上完成得更快。这是因为服务器上的总线主控设备或多或少地不断运行。因此,如果您的应用程序是基于服务器的,那么节省总线锁定对于保持性能至关重要。
修改强>
致Peter Cordes,
你说明了
......它与总线控制无关,至少在CPU上是这样,至少不是 Nehalem的
来自最新的英特尔系统编程指南:
8.1.4 LOCK操作对内部处理器高速缓存的影响
对于Intel486和Pentium处理器,LOCK#信号总是如此 在LOCK操作期间在总线上断言,即使该区域为 被锁定的内存缓存在处理器中。
对于P6和更新的处理器系列,如果是内存区域 在LOCK操作期间被锁定被缓存在处理器中 正在执行LOCK操作作为回写存储器 完全包含在缓存行中,处理器可能不会断言 总线上的LOCK#信号。相反,它将修改内存位置 在内部并允许它的缓存一致性机制来确保 操作是原子地进行的。此操作称为“缓存” 锁定。“缓存一致性机制自动阻止两个或 更多处理器缓存了相同的内存区域 同时修改该区域的数据。
在第二段中,它说
......处理器可能不会在总线上声明LOCK#信号。
现在,我不了解你,但至少对我不了解#34;可能不会"听起来很可疑不像"不会"或者"赢了"#34;。
我所产生的数字可能是也可能不正确 - 即使我偶尔会犯错误 - 但我要求你停止引用这个或那个权威"而是通过做工作来解决我的数字,发现错误或差异,从而弄脏你的手。我将相关的源代码包含在另一个帖子中(我还抱怨你的崇高,理论,基于扶手椅的评论)所以它不会让你永远开始。
例如,通过证明我
开始过度陈述锁定无争用案件的成本......
我期待你的分析。