互斥体是如何实现的?

时间:2009-09-28 08:01:42

标签: language-agnostic concurrency mutex

对于特定应用程序,某些实现是否优于其他实现?推出自己的产品有什么可赚的吗?

6 个答案:

答案 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.CompareExchangeThread.SpinWaitThread.Sleep来实现同步。在某些情况下会使用一些EventWaitHandle(内核对象)实例。

还增加了一些复杂性来支持单个线程的重入。

如果您对这个领域感兴趣并且在.NET中工作(或者至少可以阅读它),那么您可能会发现检查这个课程非常有趣。