我有一个旋转的线程,直到另一个线程更改的int是某个值。
int cur = this.m_cur;
while (cur > this.Max)
{
// spin until cur is <= max
cur = this.m_cur;
}
是否需要将this.m_cur声明为volatile以使其正常工作?由于编译器优化,它是否可能永远旋转?
答案 0 :(得分:12)
是的,这是一项艰难的要求。允许即时编译器将m_cur的值存储在处理器寄存器中,而无需从内存中刷新它。实际上x86抖动确实存在,x64抖动没有(至少是我最后一次看到它)。
volatile 关键字是抑制此优化所必需的。
易失性意味着Itanium内核,一个内存模式较弱的处理器完全不同。不幸的是,它成为了MSDN库和C#语言规范的原因。它在ARM核心上意味着什么还有待观察。
答案 1 :(得分:4)
下面的博客有一些关于c#内存模型的精彩细节。简而言之,使用volatile关键字似乎更安全。
http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/
来自下面的博客
class Test
{
private bool _loop = true;
public static void Main()
{
Test test1 = new Test();
// Set _loop to false on another thread
new Thread(() => { test1._loop = false;}).Start();
// Poll the _loop field until it is set to false
while (test1._loop == true) ;
// The loop above will never terminate!
}
}
有两种方法可以让while循环终止:使用a 锁定以保护对_loop字段的所有访问(读取和写入) 将_loop字段标记为volatile有两个原因可以读取a 非易失性字段可能会观察到陈旧的值:编译器优化 和处理器优化。
答案 2 :(得分:0)
这取决于m_cur的修改方式。如果它使用正常的赋值语句,例如m_cur--;
,那么它确实需要是volatile。但是,如果使用其中一个Interlocked操作对其进行修改,则不会因为Interlocked的方法会自动插入内存屏障以确保所有线程都获得备忘录。
通常,使用Interlocked来修改跨线程共享的原子值是更好的选择。它不仅为您处理内存障碍,而且往往比其他同步选项更快。
就是说,像其他人一样说投票循环非常浪费。最好暂停需要等待的线程,让任何正在修改m_cur的人负责在时机成熟时将其唤醒。根据您的具体需求,Monitor.Wait() and Monitor.Pulse()和AutoResetEvent可能都非常适合此任务。