9

我有一个线程旋转,直到另一个线程更改的 int 是某个值。

int cur = this.m_cur;
while (cur > this.Max)
{
    // spin until cur is <= max
    cur = this.m_cur; 
}

是否需要将 this.m_cur 声明为 volatile 才能正常工作?由于编译器优化,这是否有可能永远旋转?

4

3 回答 3

12

是的,这是一个硬性要求。允许即时编译器将 m_cur 的值存储在处理器寄存器中,而无需从内存中刷新它。x86 抖动实际上有,x64 抖动没有(至少在我最后一次查看时)。

需要volatile关键字来抑制这种优化。

易失性在 Itanium 内核上意味着完全不同的东西,这是一种内存模型较弱的处理器。不幸的是,这就是它成为 MSDN 库和 C# 语言规范的原因。这对 ARM 内核意味着什么还有待观察。

于 2012-04-25T00:53:02.453 回答
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 循环: 使用锁来保护对 _loop 字段的所有访问(读取和写入) 将 _loop 字段标记为易失性 读取非易失性字段可能观察到的原因有两个过时的值:编译器优化和处理器优化。

于 2012-04-25T00:26:19.603 回答
0

这取决于如何修改 m_cur。如果它使用的是普通的赋值语句,例如m_cur--;,那么它确实需要是 volatile 的。但是,如果使用Interlocked操作之一对其进行修改,则不会,因为 Interlocked 的方法会自动插入内存屏障以确保所有线程都获得备忘录。

通常,使用 Interlocked 修改跨线程共享的原子值是更可取的选择。它不仅为您解决了内存障碍,而且还往往比其他同步选项快一点。

也就是说,就像其他人所说的那样,轮询循环非常浪费。最好暂停需要等待的线程,让修改 m_cur 的人负责在时机成熟时将其唤醒。Monitor.Wait() 和 Monitor.Pulse()以及AutoResetEvent 都可能非常适合该任务,具体取决于您的特定需求。

于 2012-04-25T00:59:52.953 回答