Volatile.Read/Write的理解

17

我正在尝试理解C#中的Volatile类。

据我的阅读:

  • Volatile.Write方法在调用点强制将位置上的值写入。此外,任何早期程序顺序的加载和存储都必须发生在对Volatile.Write的调用之前。

  • Volatile.Read方法在调用点强制从位置中读取值。此外,任何较晚的程序顺序加载和存储都必须发生在对Volatile.Read的调用之后。

这是否意味着在以下情况下:

internal sealed class ThreadsSharingData {    
    private Int32 m_flag = 0;
    private Int32 m_value = 0;
    // This method is executed by one thread
    public void Thread1() {        
        // Note: 5 must be written to m_value before 1 is written to m_flag
        m_value = 5;
        Volatile.Write(ref m_flag, 1);        
    }

    // This method is executed by another thread
    public void Thread2() {        
        // Note: m_value must be read after m_flag is read
        if (Volatile.Read(ref m_flag) == 1)
        Console.WriteLine(m_value);        
    }    
}

在执行写入m_flag之前,CPU会等待Volatile.Write(ref m_flag, 1);命令的执行吗?

这如何帮助线程同步?


1
只有在具有弱内存模型的处理器上才相关,其中获取和释放语义很重要。现在几乎没有这种情况了,除了 ARM 核心。这不是适当同步的替代品,编写无需同步的线程安全代码是一个非常高级的概念。否则,已经在这个问题中很好地讨论过了:https://dev59.com/nmUp5IYBdhLWcg3wzp1k。 - Hans Passant
2个回答

11
CPU会在执行Volatile.Write(ref m_flag, 1)前等待指令吗? 比较好的表述方式是:如果其他任何线程看到m_flag设置为1,则它们也会看到m_value设置为5。这有助于确保程序的正确性,但不一定有助于线程同步。如果不使用volatile读/写,则编译器/运行时/CPU可能会重新排序Thread1方法中的两个指令,并且程序可以打印0、5或根本不打印。使用volatile读/写,程序将打印5或根本不打印,但绝不会打印0,这是预期的行为。

5

这有助于线程同步吗?

它不能帮助线程同步,也就是不能设置它们命令执行的顺序。但是,在某些情况下,当特定的顺序对程序逻辑很重要时,可以确保并发线程以特定顺序观察内存中的值的更改。

CPU是否在执行Volatile.Write(ref m_flag, 1);之前等待命令开始写入m_flag

不,写入m_value的命令已经执行。但是,其结果可能不会在CPU核心外部可见 - 特别是在不同核心上运行的线程可能会在写入5的命令执行完毕后从m_value读取旧值。这是因为新值可能在CPU的缓存中,而不在内存中。

如果您写

m_value = 5;
m_flag = 1;

在it技术方面,如果你使用 Volatile.Write(ref m_flag, 1),那么另一个核心可能会按照不同的顺序看到写入:首先它会看到 m_flag 变成了 1,然后才会看到 m_value 变成了 5。如果你的另一个线程使用 m_flag 的值来判断 m_value 的有效性,那么逻辑可能会出错:例如,Thread2 可能偶尔会打印零。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接