对于特定应用程序,某些实现是否优于其他实现?推出自己的产品有什么可赚的吗?
答案 0 :(得分:29)
查看维基百科上Test-and-set机器指令的说明,其中提到了如何在机器级别实现原子操作。我可以想象大多数语言级互斥实现都依赖于机器级支持,例如Test-and-set。
答案 1 :(得分:22)
基于Adamski的test-and-set
建议,您还应该考虑“快速用户空间互斥”或futexes的概念。
Futexes具有理想的属性,在锁定或解锁无竞争互斥锁的常见情况下,它们不需要内核系统调用。在这些情况下,用户模式代码成功使用原子compare and swap (CAS)操作来锁定或解锁互斥锁。
如果CAS失败,则互斥锁是争用的,并且Linux下的内核系统调用 - sys_futex
必须用于等待互斥锁(在锁定情况下)或唤醒其他线程(在解锁案例)。
如果你真的想要自己实现这个,请确保你也阅读了Ulrich Drepper的paper。
答案 2 :(得分:8)
mutex最好在操作系统的内核中运行,同时尽可能缩短其周围的代码量,这样可以避免在任务切换到另一个进程时被切断。因此,确切的实施是一个秘密。虽然这并不复杂。它基本上是一个具有布尔字段的对象,它可以获取和设置。
围绕基本的互斥逻辑,有包装器将它包装在一个对象中。然后有更多的包装器对象使它在内核之外可用。然后是另一个使它在.NET中可用的包装器。然后,一些程序员将根据自己的逻辑需求编写自己的包装代码。包装纸周围的包装纸真的让它们变得模糊不清。
现在,有了关于互斥锁内部的基本知识,我希望你将使用一个依赖于内核和下面硬件的实现。这些将是最可靠的。 (如果硬件支持这些。)如果您使用的互斥锁在此内核/硬件级别不起作用,那么它仍然可靠但我建议不要使用它,除非别无选择。
据我所知,Windows,Linux和.NET都将在内核/硬件级别使用互斥锁。
我链接的维基百科页面详细介绍了内部逻辑和可能的实现。优选地,互斥体由硬件控制,从而使得互斥体的整体获得/设置为indivisible step。 (只是为了确保系统不会在中间切换任务。)
答案 3 :(得分:2)
Interlocked.CompareExchange
足以实现自旋锁。尽管如此,这很难做到。有关所涉及的细微差别的示例,请参阅Joe Duffy's blog。
答案 4 :(得分:1)
一些汇编程序来演示原子锁定:
; BL is the mutex id
; shared_val, a memory address
CMP [shared_val],BL ; Perhaps it is locked to us anyway
JZ .OutLoop2
.Loop1:
CMP [shared_val],0xFF ; Free
JZ .OutLoop1 ; Yes
pause ; equal to rep nop.
JMP .Loop1 ; Else, retry
.OutLoop1:
; Lock is free, grab it
MOV AL,0xFF
LOCK CMPXCHG [shared_val],BL
JNZ .Loop1 ; Write failed
.OutLoop2: ; Lock Acquired
答案 5 :(得分:-1)
我使用Reflector.NET反编译System.Threading.ReaderWriterLockSlim
的源代码,该代码已添加到最新版本的.NET框架中。
它主要使用Interlocked.CompareExchange
,Thread.SpinWait
和Thread.Sleep
来实现同步。在某些情况下会使用一些EventWaitHandle
(内核对象)实例。
还增加了一些复杂性来支持单个线程的重入。
如果您对这个领域感兴趣并且在.NET中工作(或者至少可以阅读它),那么您可能会发现检查这个课程非常有趣。