【问题标题】:volatile with release/acquire semantics具有释放/获取语义的易失性
【发布时间】:2011-06-29 19:53:37
【问题描述】:

从 Java 5 开始,volatile 关键字具有释放/获取语义以使副作用对其他线程可见(包括对 非易失性 变量的赋值!)。以这两个变量为例:

int i;
volatile int v;

请注意,i 是一个常规的非易失性变量。想象一下线程 1 执行以下语句:

i = 42;
v = 0;

在稍后的某个时间点,线程 2 执行以下语句:

int some_local_variable = v;
print(i);

根据 Java 内存模型,v 在线程 1 中的写入和 v 在线程 2 中的读取确保线程 2 看到在线程 1 中执行的对 i 的写入,因此值为 42被打印出来了。

我的问题是:volatile 在 C# 中是否具有相同的发布/获取语义?

【问题讨论】:

标签: c# java volatile memory-model java-memory-model


【解决方案1】:

C# 中“volatile”的语义在C# 6.0 specification 的第 7.10 和 14.5.4 节中定义。我鼓励您在规范中查找它们,而不是在这里复制它们,然后确定使用“易失性”太复杂和危险,然后重新使用锁。这就是我一直做的事情。

请参阅 C# 6.0 规范中的“7.10 Execution order”和“14.5.4 Volatile fields”。


C# 5.0 specification,见“3.10 执行顺序”和“10.5.3 易失字段”

【讨论】:

  • 试图让 C++ 程序员远离“复杂和危险”的细节可能会产生相反的效果 ;)
  • @Fred 所以你只是对 volatile 感兴趣,因为它既复杂又危险?
  • @David:我只是不喜欢不精确的知识,我的印象是volatile 是有史以来最容易被误解的关键字之一。我想知道东西是如何工作的,仅此而已:)
  • @Fred 我想知道东西是如何工作的,但我觉得没有必要为了它而学习东西。也许我太老了!
【解决方案2】:

好吧,我相信它可以确保如果 some_local_variable 被读取为0(由于写入v),i 将被读取为 42。

棘手的部分是“在以后的某个时间点”。虽然通常在“刷新”写入方面讨论波动性,但这并不是规范中实际定义的方式(Java 或 C#)。

来自 C# 4 语言规范,第 10.5.3 节:

对于非易失性字段,重新排序指令的优化技术可能会在多线程程序中导致意外和不可预知的结果,这些程序在没有同步的情况下访问字段,例如锁语句(第 8.12 节)提供的。这些优化可以由编译器、运行时系统或硬件来执行。对于 volatile 字段,此类重新排序优化受到限制:

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

然后有一个示例与您的非常相似,但以从 volatile 变量读取的值为条件。

和 Eric 一样,我强烈避免自己依赖 volatile。这很难推理,最好留给世界上的乔·达菲/斯蒂芬·图布斯。

【讨论】:

  • 您能否更具体地说明您对 Java 中“刷新”写入的含义?您是在暗示我的代码甚至不能在 Java 中运行吗?
  • @Fred 好吧,如果我们相信 Jon 的小引语,那么 C# 似乎确实保证了写入的释放语义并获得读取的语义,这意味着是的,该示例在 java 和 C# 中都可以正常工作..现在您仍然需要阅读重要部分,以确保不会错过一些极端情况。虽然我完全可以理解为什么避免 volatile(以及信号量和所有其他低级线程的东西)通常不是坏主意,但我也讨厌在我的知识中存在漏洞——仅仅因为你知道如何使用 volatile,不不意味着你必须!
  • @Fred:问题是您还没有定义“一段时间后”的意思。您可以保证如果线程 2 看到对 volatile 变量的写入,那么它会看到对非易失性变量的写入。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-29
  • 1970-01-01
  • 2010-11-02
  • 2011-01-03
  • 1970-01-01
相关资源
最近更新 更多