【问题标题】:Usage of Volatile.Write() instead of volatile in C#在 C# 中使用 Volatile.Write() 而不是 volatile
【发布时间】:2017-01-30 10:11:09
【问题描述】:

假设以下代码(注意这只是示例代码):

public async void UnsafeMethod()
{
  int unsafeValue = 0;

  Task.Run(() => unsafeValue = 42 ); // set unsafeValue to some value
  await Task.Delay(10);              // wait for some time

  Assert.AreEqual(42, unsafeValue);  // check if unsafeValue has the new value
}

(这里假设CPU空闲,立即执行任务。)

由于任务将在新线程上执行,unsaveValue 的新值可能对其他线程不可见,因为可能存在缓存问题。如果我想让更改可见,我将不得不使用 volatile 并将局部变量设为字段:

private volatile int safeValue = 0;

public async void SafeMethod()
{
  Task.Run(() => safeValue = 42 ); // set safeValue to some value
  await Task.Delay(10);            // wait for some time

  Assert.AreEqual(42, safeValue);  // check if safeValue has the new value
}

(再次假设任务立即执行。)

我现在的问题是,如果以下内容会在保留局部变量的同时做同样的事情(我知道,编译器无论如何都会使它成为一个字段......):

public async void AnotherMethod()
{
  int safeValue = 0;

  Task.Run(() => Volatile.Write(ref safeValue, 42); // set safeValue to some value
  await Task.Delay(10);                             // wait for some time

  Assert.AreEqual(42, safeValue);                   // check if safeValue has the new value
}

(任务立即执行。)

文档对我来说并不完全清楚。 https://msdn.microsoft.com/en-us/library/system.threading.volatile(v=vs.110).aspx

它说:

在多处理器系统上,易失性写操作可确保 写入内存位置的值立即对所有人可见 处理器。

这就是我想要的。但是,它也指出:

将指定值写入指定字段。在系统上 需要它,插入一个内存屏障,防止处理器 重新排序内存操作如下:如果出现读取或写入 在代码中的这个方法之前,处理器不能在之后移动它 这个方法。

在方法描述中,我不确定这是否是我想要的。

又是我的问题:最后一段代码是否会按照我的意愿执行(使写入立即对其他线程可见)?

【问题讨论】:

  • 您肯定想使用Volatile.Write() 而不是volatile。其实Eric Lippert says you should avoid volatile.
  • 恕我直言,Assert.AreEqual(42, safeValue); 不知道对Volatile.Write 的调用,因此safeValue 可能会被缓存。我想Volatile.Read(当你打电话给Assert.AreEqual时)会是首选......
  • Matteo 是正确的 - 您需要在每次写入时使用 Volatile.Write(),在读取时使用 Volatile.Read()(线程共享字段)。
  • 谢谢大家,我使用了 Volatile.Write 和 Volatile.Read 如你所说。我不能肯定地说它正在工作(它正在工作到现在)。
  • 你认为编译器为什么将局部变量作为字段?

标签: c# multithreading volatile


【解决方案1】:

正如 Matteo UmiliMatthew Watson 在 cmets 中提到的,当您读取非易失性字段时,您必须使用 Volatile.Read。那是因为同步始终是一个 2 人游戏。

在这种情况下,Volatile.Write 在写入之后插入存储释放屏障,Volatile.Read 在读取之前设置加载获取屏障。

【讨论】:

  • 我仍然不确定是否包括硬件部分的“同步”(即是否立即将缓存写回内存)。从软件的角度来看,它是有效的。但是,我不确定如何保证或测试物理内存分配实际上没有被缓存。
  • 硬件中发生的事情取决于 JIT 和特定的 CPU。即使您使用Interlocked.MemoryBarrier,它当前转换为刷新写入缓冲区的锁定操作,它也不能保证其他 CPU 将“立即”看到写入并考虑它们,因为它们前面有读取缓冲区.所以不要在硬件层面上考虑它。 Volatile 是关于原子性和内存屏障的。想想内存访问如何相互关联,以及障碍如何阻止它们重新排序。
猜你喜欢
  • 2023-02-07
  • 2014-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-08
  • 1970-01-01
  • 2011-06-20
相关资源
最近更新 更多