我是否需要此字段是可变的

本文关键字:字段 是否 | 更新日期: 2023-09-27 18:32:20

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

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

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

我是否需要此字段是可变的

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

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

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

下面的博客有一些关于 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字段标记为易失性 有两个原因可以解释为什么读取 非易失性字段可能会观察到过时的值:编译器优化 和处理器优化。

这取决于

m_cur的修改方式。如果它使用正常的赋值语句,例如 m_cur--; ,那么它确实需要易失性。 但是,如果使用联锁操作之一对其进行修改,则不会,因为 Interlock 的方法会自动插入内存屏障以确保所有线程都获取备忘录。

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

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