Mar*_*ell 22 .net c# multithreading monitor volatile
人们普遍接受(我相信!)a lock会强制重新加载字段中的任何值(基本上充当内存屏障或栅栏 - 我在这方面的术语有点松散,我害怕),结果那些只能在一个内部访问过的字段lock本身并不需要volatile.
(如果我错了,就说!)
这里提出了一个很好的评论,质疑如果代码执行是否也是如此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
}
}
Run Code Online (Sandbox Code Playgroud)
显然我可以做到volatile,或者我可以把它移出去,以便Monitor每当它发出脉冲时我退出并重新进入,但我很想知道是否有必要.
Mic*_*urr 18
由于该Wait()方法是释放并重新获取Monitor锁,如果lock执行内存栅栏语义,那么Monitor.Wait()也将如此.
希望能够解决您的意见:
锁定行为Monitor.Wait()是在文档(http://msdn.microsoft.com/en-us/library/aa332339.aspx)中,重点是:
当一个线程调用Wait时,它释放对象的锁并进入对象的等待队列.对象的就绪队列中的下一个线程(如果有)获取锁并且独占使用该对象.所有调用的线程都会
Wait保留在等待队列中,直到它们收到来自Pulse的信号或PulseAll由锁的所有者发送.如果Pulse发送,则只有等待队列头部的线程受到影响.如果PulseAll发送,则等待该对象的所有线程都会受到影响.当接收到信号时,一个或多个线程离开等待队列并进入就绪队列.准备队列中的线程被允许重新获取锁.当调用线程重新获取对象上的锁时,此方法返回.
如果您询问有关lock/ acquire 是否Monitor意味着内存障碍的参考,ECMA CLI规范说明如下:
12.6.5锁和线程:
获取锁(
System.Threading.Monitor.Enter或进入同步方法)将隐式执行易失性读操作,并且释放锁(System.Threading.Monitor.Exit或保留同步方法)将隐式执行易失性写操作.见§12.6.7.
12.6.7易失性读写:
易失性读取具有"获取语义",这意味着保证在CIL指令序列中的读取指令之后发生的对存储器的任何引用之前发生读取.易失性写入具有"释放语义",这意味着写入保证在CIL指令序列中的写入指令之前的任何存储器引用之后发生.
此外,这些博客条目还有一些可能感兴趣的细节: