22

人们普遍接受(我相信!) alock将强制重新加载字段中的任何值(本质上充当内存屏障或栅栏 - 恐怕我在这方面的术语有点松散),结果那些只在 a 内部访问lock的字段本身不需要是volatile.

(如果我已经错了,就说吧!)

这里提出了一个很好的评论,质疑如果代码执行 a 是否也是如此Wait()- 即一旦它被Pulse()d,它是否会从内存中重新加载字段,或者它们是否可以在寄存器中(等)。

或者更简单地说:该字段是否需要volatile确保在 a 之后恢复时获得当前值Wait()

看着反射器,Wait调用到ObjWait,即managed internalcall(与 相同 Enter)。

有问题的场景是:

bool closing;
public bool TryDequeue(out T value) {
    lock (queue) { // arbitrary lock-object (a private readonly ref-type)
        while (queue.Count == 0) {
            if (closing) {       // <==== (2) access field here
                value = default(T);
                return false;
            }
            Monitor.Wait(queue); // <==== (1) waits here
        }
        ...blah do something with the head of the queue
    }
}

显然我可以成功,或者我可以将它移出,以便每次它被脉冲时volatile我退出并重新进入,但我很想知道是否有必要Monitor

4

3 回答 3

18

由于该Wait()方法是释放和重新获取Monitor锁,如果lock执行内存栅栏语义,那么Monitor.Wait()也将执行。

希望解决您的评论:

的锁定行为Monitor.Wait()在文档(http://msdn.microsoft.com/en-us/library/aa332339.aspx)中,重点补充说:

当一个线程调用Wait时,它会释放对象上的锁并进入对象的等待队列。对象就绪队列中的下一个线程(如果有的话)获取锁并独占使用该对象。所有调用的线程都Wait保留在等待队列中,直到它们收到来自 Pulse 或PulseAll锁的所有者发送的信号。如果Pulse发送,则只有等待队列头部的线程受到影响。如果PulseAll发送,所有等待对象的线程都会受到影响。当收到信号时,一个或多个线程离开等待队列,进入就绪队列。就绪队列中的线程被允许重新获取锁。

当调用线程重新获得对象上的锁时,此方法返回

如果您询问关于lock/acquired是否Monitor意味着内存屏障的参考,ECMA CLI 规范说明如下:

12.6.5 锁和线程:

获取锁(System.Threading.Monitor.Enter或进入同步方法)应隐式执行易失性读操作,释放锁(System.Threading.Monitor.Exit或离开同步方法)应隐式执行易失性写操作。请参阅第 12.6.7 节。

12.6.7 易失性读写:

易失性读取具有“获取语义”,这意味着读取保证发生在 CIL 指令序列中读取指令之后发生的任何对内存的引用之前。易失性写入具有“释放语义”,这意味着写入保证发生在 CIL 指令序列中写入指令之前的任何内存引用之后。

此外,这些博客条目有一些可能感兴趣的细节:

于 2010-03-12T08:09:35.027 回答
4

根据 Michael Burr 的回答,不仅Wait释放并重新获取锁,而且这样做是为了让另一个线程可以取出锁以检查共享状态并调用Pulse. 如果第二个线程没有取出锁,那么Pulse将抛出。如果他们不这样做Pulse,第一个线程Wait将不会返回。因此,任何其他线程对共享状态的访问都必须发生在适当的内存限制场景中。

因此,假设这些Monitor方法是根据本地可检查规则使用的,那么所有内存访问都发生在锁内,因此只有自动内存屏障支持lock是相关/必要的。

于 2010-03-12T08:15:22.827 回答
1

也许这次我可以帮助你......而不是使用volatile可以Interlocked.Exchange与整数一起使用的 a 。

if (closing==1) {       // <==== (2) access field here
    value = default(T);
    return false;
}

// somewhere else in your code:
Interlocked.Exchange(ref closing, 1);

Interlocked.Exchange是一种同步机制,volatile不是...我希望这是值得的(但您可能已经考虑过这一点)。

于 2010-03-12T08:26:20.930 回答