当前位置: 首页 > 知识库问答 >
问题:

为什么C#volatile不保护写-读重新排序?

龚招
2023-03-14

根据这本在线书籍,C#中的易失性关键字不能防止重排序写操作后跟读操作。它给出了这个例子,其中ab最终都可以设置为0,尽管xy易失性

class IfYouThinkYouUnderstandVolatile
{
  volatile int x, y;
 
  void Test1()        // Executed on one thread
  {
    x = 1;            // Volatile write (release-fence)
    int a = y;        // Volatile read (acquire-fence)
    ...
  }
 
  void Test2()        // Executed on another thread
  {
    y = 1;            // Volatile write (release-fence)
    int b = x;        // Volatile read (acquire-fence)
    ...
  }
}

这似乎符合10.5.3中的规范:

对易失性字段的读取称为易失性读取。易失性读取具有“获取语义”;也就是说,在指令序列中,它保证发生在对内存的任何引用之前,而对内存的任何引用发生在它之后。

对易失性字段的写入称为易失性写入。易失性写入具有“释放语义”;也就是说,它保证在指令序列中写入指令之前的任何内存引用之后发生。

这是什么原因?是否有一个我们不介意重新排序写读操作的用例?

共有3个答案

丁文轩
2023-03-14

也许来不及回答。。。但我环顾四周,看到了这个。Volatile read实际上是对类似以下方法的调用:

public static int VolatileRead(ref int address)
{
    int num = address;
    Thread.MemoryBarrier();
    return num;
}

易失性写入如下:

public static int VolatileWrite(ref int address, int value)
{
    Thread.MemoryBarrier();
    adrdress = value;
}

指令MemoryBarrier() 是阻止重新排序的代码<代码>MemoryBarrier() 确保前指令在后指令之前执行。当VW之后是VR时,您将拥有:

Thread.MemoryBarrier();
adrdress = value; //this line may be reordered with the one bellow
int num = address;//this line may be reordered with the one above
Thread.MemoryBarrier();
return num;
秦宁
2023-03-14

在x86/x64 arch上,使用易失性读取防止易失性写入将非常昂贵。这是因为名为存储缓冲区的写优化。Java就是这样做的,Java中的易失性写操作实际上是CPU指令级的完全内存屏障。

钦高峯
2023-03-14

Volatile不保证独立易失性变量的读写不会被重新排序,它只保证读取得到最新的值(非缓存)。(保证对单个变量的读写保持顺序)

http://msdn.microsoft.com/en-us/library/x13ttww7(v=vs.71)。aspx

系统总是在请求的点读取易失性对象的当前值,即使之前的指令要求来自同一对象的值。此外,对象的值在赋值时立即写入。

volatile修饰符通常用于由多个线程访问的字段,而不使用lock语句来序列化访问。使用volatile修饰符可以确保一个线程检索另一个线程写入的最新值。

每当您有多个依赖操作时,就需要使用其他一些同步机制。通常使用锁,这是最简单的方法,仅在滥用或极端情况下会造成性能瓶颈。

 类似资料:
  • 主要内容:可见性,原子性,举个例子首先要了解的是,volatile可以保证可见性和顺序性,这些都很好理解,那么它为什么不能保证原子性呢? 可见性 可见性与Java的内存模型有关,模型采用缓存与主存的方式对变量进行操作,也就是说,每个线程都有自己的缓存空间,对变量的操作都是在缓存中进行的,之后再将修改后的值返回到主存中,这就带来了问题,有可能一个线程在将共享变量修改后,还没有来的及将缓存中的变量返回给主存中,另外一个线程就对共享变量

  • 主要内容:可见性,原子性,举个例子首先要了解的是,volatile可以保证可见性和顺序性,这些都很好理解,那么它为什么不能保证原子性呢? 可见性 可见性与Java的内存模型有关,模型采用缓存与主存的方式对变量进行操作,也就是说,每个线程都有自己的缓存空间,对变量的操作都是在缓存中进行的,之后再将修改后的值返回到主存中,这就带来了问题,有可能一个线程在将共享变量修改后,还没有来的及将缓存中的变量返回给主存中,另外一个线程就对共享变量

  • 我理解读-获取(在它之后不进行后续读/写操作的重新排序)和写-释放(在它之前不进行读/写操作的重新排序)。我的q是:- 在读获取的情况下,它前面的写操作会被刷新吗 另外,在Java中,读获取和volatile读相同,写释放和volatile写相同吗? 为什么这很重要,让我们以写发布为例... 在这一点上,x是2还是1?这里,考虑准备好成为挥发性的。我知道挥发性之前的所有商店都将首先可见...然后只

  • 首先,我知道volatile不会使多个操作(如)成为原子操作。这个问题是关于单个读或写操作的。 我最初的理解是,volatile只是执行了一个内存屏障(即其他线程将能够看到更新的值)。 现在我已经注意到JLS第17.7节说volatile还提供了一个读取或写入原子。例如,给定两个线程,两个线程都将不同的值写入,那么x最终将恰好表示其中一个值。 我很好奇这怎么可能。在一个32位系统上,如果两个线程并

  • 80386保护机制有助于找出和调试程序的BUG。80386支持很复杂的程序,它们可以包含成百上千的程序模块。在这样的程序中,关键问题是如何快而有效的找出程序的BUG,并将之造成的损坏降到最小。为了能让程序便于调试和生成高质量的产品,80386包含了检验内存访问、指令执行等很多保护机制。根据系统设计者的设计目标,这些保护机制可以使用,也可以忽略。

  • 当我用公共方法重写受保护的方法时,Java编译器不会抱怨。这里到底发生了什么?由于父方法的可见性较低,它是覆盖还是隐藏父方法?