Monitor.Enter(object,ref bool) 锁获取的实现和顺序

Monitor.Enter(object,ref bool) Implementation and Order of Lock Acquisition

Reference Source可以看出上述方法的实现方式如下:

 public static void Enter(Object obj, ref bool lockTaken)
    {
        if (lockTaken)
            ThrowLockTakenException();

        ReliableEnter(obj, ref lockTaken);
        Contract.Assert(lockTaken);
    }

我对 'if' 声明有疑问 - 我知道它很重要,无需解释。相信这个说法也引出了一个很微妙的问题。

考虑以下场景: 一个线程调用该方法并评估 if 语句,此时发生上下文切换,另一个线程调用该方法,成功获得锁。

这是一个可行的场景,表明获取锁的顺序不一定与方法调用的顺序相同。

虽然在某些情况下它可能不会真正产生影响,但在其他情况下它可能至关重要,所以我的问题是:

1) 我是不是遗漏了什么,或者 Monitor class 不太适合在此类事件不可接受的场景中使用?

2) ReliableEnter 是否有可能遇到类似情况?

3)是否有同步机制保证方法调用的顺序与获取锁的顺序一致?

我不认为你描述的是一个实际问题。 if 语句与锁的状态无关。它只是作为输入验证,以确保第二个参数的值为 false。

想象一下 if 语句不存在。例如,在 return 地址被压入堆栈之后和执行跳转指令之前,上下文切换仍然可能发生。

锁用于防止程序进入不一致的状态。但是,如果关键部分的结构正确,那么获取锁的顺序并不重要。在您描述的示例中,程序将简单地继续,就好像第二个线程已获得锁一样,并且第一个线程将在 lockTaken 参数中具有 false

One thread calls into the method and evaluates the if statement, at which point a context switch occurs and another calls into the method, successfully obtaining the lock.

完全有可能。

或者,可能没有上下文切换,但这种情况仍然会发生。两个线程都通过 "if" 并进入 ReliableEnter,两者都没有上下文切换,一个获胜。你能想到发生这种情况的场景吗?

This is a feasible scenario which suggests that the order of lock acquisition is not necessarily the same as the order of method invocation.

记得三位裁判的故事。

三位裁判正在吃午饭。第一个说 "I calls them as I sees them"。第二个说 "I calls them as they are"。第三个说 "They ain't nothing until I calls them."

第一位裁判认为观察是不完美的,人们可能不同意投球是球还是好球。第二种认为存在一个一致的物理世界,并且他们拥有关于它的准确信息;一个球是球还是好球是可以客观判断的。第三种认为,无论世界是否一致和可知,棒球规则规定,裁判员称击中的任何击球都是击球

你是第二个裁判。您认为在多线程世界中确实存在 "the order in which method calls occur" 这样的东西,并且有一种方法可以了解它。但实际上,世界更像是第一位裁判:每个人都可以看到一个不同的世界,并对此持不同意见。锁更像是第三个裁判:锁是将秩序强加于无序世界的东西。调用方法的顺序是什么? 拿一把锁找出来.

摒弃多线程世界中事物发生的顺序一致的神话。 没有这样一致的顺序。每个处理器都可以在内存模型约束内根据需要重新排序代码。每个线程都可以看到不同的读写顺序。

每个线程都同意的是获取锁的顺序,因此考虑 "the order" 调用方法的顺序是明智的。

那么,锁的发生顺序是否可以不同于方法调用的顺序?第一位裁判说 "no one can know for sure"。第二个说 "yes"。第三个说 "there is no order at all until a lock is taken".

我同意第三位裁判的看法。是否可以按与调用方法不同的顺序获取锁?由于 强制执行该命令的唯一方法是获取锁,这并不是一个明智的问题。

1) Am I missing something, or is the Monitor class less suited for use in scenarios where such occurrences are unacceptable?

这种看法是错误的。

如果我们继续相信调用发生的顺序是虚构的,并且您的程序的正确性取决于以完全神话般的顺序获取锁,那么该程序就有错误。锁 在 "the order that the calls happen" 中使用,故事结束,如果您依赖于此,那么您依赖于没有人做出且经常被违反的保证。

2) Is it possible that ReliableEnter suffers from a similar condition?

我不明白这个问题。您的意思是,如果我们将对 Enter 的每个调用都替换为对 ReliableEnter 的调用,我们还会遇到同样的问题吗?是的,绝对。

3)Is there a synchronization mechanism which ensures that the order of method invocation is the same as the order of lock acquisition?

没有这种独立于锁的顺序,所以没有。

I have an issue is with the 'if' statement- I understand it's importance, there's no need to explain that. I believe this statement also introduces a very subtle issue.

你的想法是错误的。这个问题是由于在多线程世界中没有 "the order in which method calls happen" 这样的东西而引入的。 "if" 与它无关。

此外,即使在我们可以从锁中提取排序信息的世界中,锁也不能保证公平。如果你有十个线程都在等待一个锁,那么 C# 语言没有强加的要求,已经是 "waiting longest" 或 "called first" 的线程——不管那意味着什么——是可以访问的线程接下来是显示器。操作系统有很大的自由度来安排它认为合适的线程,并且它有权无限期地饿死一个线程。

现在,我上面所说的有很多半真半假的地方。世界的实际状况非常混乱。如果您想知道真相,那么您应该阅读 C# 规范中关于 多线程程序中的副作用排序 的部分,其中描述了保证观察到的事件,特别是命令:锁、线程的创建、某些类型的读写、异常和构造函数调用。在某些情况下,订购保证非常薄弱。

当出现这种情况时,我总是这样做,我建议您阅读我关于如何在没有锁的世界中发生重新排序的文章。看看你能不能解决我在第二部分提出的难题。

http://blog.coverity.com/2014/03/12/can-skip-lock-reading-integer/ http://blog.coverity.com/2014/03/26/reordering-optimizations/