【问题标题】:Why does Volatile.Read take ref parameter?为什么 Volatile.Read 带 ref 参数?
【发布时间】:2017-10-20 12:26:00
【问题描述】:

Volatile.Read 的实现只是在读取之后插入了一个内存屏障:

public static int Read(ref int location)
{
    var value = location;
    Thread.MemoryBarrier();
    return value;
}

因此,这样的方法的使用......

return Volatile.Read(ref _a) + Volatile.Read(ref _b);

...相当于:

var a = _a;
Thread.MemoryBarrier();
var b = _b;
Thread.MemoryBarrier();
return a + b;

鉴于上述情况,如果参数不是ref,则结果行为是否相同?

public static int Read(int value)
{
    Thread.MemoryBarrier();
    return value;
}

我猜ref 参数只是用来防止程序员传递变量以外的东西,例如Volatile.Read(2 + 3)。传入变量时,任何人都可以看到ref 的任何其他原因吗?

【问题讨论】:

  • 因为否则您正在阅读副本?
  • 是的,但这有关系吗?在读取原件和后续使用之间是否仍会插入内存屏障?

标签: c# .net volatile ref


【解决方案1】:

这不是 真实 代码在抖动完成后的样子。 Volatile.Read() 是一个内在的。一个昂贵的词,意味着抖动根本不会编译该方法,而是用特定于处理器的机器代码版本代替它。您找到的代码只是一个占位符,可以用作机器上的后备,而不会产生像样的抖动。理论上。

您可以通过“调试”>“Windows”>“反汇编”查看计算机上的内容。您必须切换到发布版本并使用工具 > 选项 > 调试 > 常规 > 取消选中“抑制 JIT 优化”。大多数程序员会对他们看到的内容感到满意:

04CF0450  mov         ecx,dword ptr ds:[7C43ACh] 

或者换句话说:什么都没有。只是一个没有障碍的普通内存读取。 Intel/AMD 内核具有不需要屏障的强大内存模型。但是,比如说,ARM 内核确实需要并且将需要变量的地址。 Itanium 也很弱,但它的抖动已停止。

请注意,虽然您可能会很高兴,但这也是一个相当坏的消息。您基本上不能依靠在您的开发机器上测试您的程序并得出结论它足够好。或者换句话说,没有办法告诉你应该使用 Volatile 但你忘记了。只有在设备上运行你的程序才会提示你出错了。跳过lock确实需要很大的勇气:)

还有几个类似的内在函数,例如 Math.Sqrt()。大多数处理器都将其作为内置的机器代码指令。

【讨论】:

  • 感谢您的精彩解释!但是,像 ARM 这样的弱架构真的需要变量的地址吗?易失性读取应该只被期望防止它们之后的内存操作的重新排序,而不是之前的。因此,Volatile.Read 除了在其调用和变量的后续使用之间插入内存屏障之外,不需要做任何事情,即使在弱架构上也是如此。
  • 你总是需要变量的地址来做一个易失性读取,0x7C43AC在上面的sn-p中。请注意它是如何简单地阻止您传递一个无法工作的属性,并且您会得到一个友好的编译时错误。而不是抖动必须在运行时弄清楚它是一个左值并在不是时生成一个异常。
【解决方案2】:

因为在 .NET 中参数默认是按值传递的。

【讨论】:

    猜你喜欢
    • 2011-02-21
    • 2010-11-23
    • 2017-11-11
    • 1970-01-01
    • 2013-10-15
    • 2010-10-06
    • 1970-01-01
    • 2018-11-25
    • 1970-01-01
    相关资源
    最近更新 更多