请注意我并不是要求用其他方法(如锁定)替换volatile
,我问的是volatile
性质 - 因此我在意义上使用“需要”,{{ 1}}或没有volatile
。
考虑这样的情况:一个线程只写入变量volatile
(x
)而另一个线程只读取它。两种情况下的访问都是直接的。
需要Int32
来避免缓存,对吗?
但是,如果对volatile
的访问是间接的 - 例如通过属性:
x
因此,两个线程现在仅使用int x;
int access_x { get { return x; } set { x = value; } }
,而不是access_x
。 x
是否需要标记为x
?如果是,那么在不再需要volatile
时会有一些间接限制吗?
更新:考虑读者的此类代码(无写入):
volatile
在第二个if (x>10)
...
// second thread changes `x`
if (x>10)
...
编译器中,可以评估使用旧值,因为if
可能位于缓存中而没有x
则无需重新获取。我的问题是关于这种变化:
volatile
让我们说if (access_x>10)
...
// second thread changes `x`
if (access_x>10)
...
跳过volatile
。会发生什么/会发生什么?
答案 0 :(得分:2)
x
是否需要标记为volatile
?
是的,没有(但主要是肯定的)。
"是",因为从技术上讲,你在这里没有任何保证。允许编译器(C#和JIT)进行他们认为合适的任何优化,只要优化不会在单线程中执行时改变代码的行为。一个明显的优化是省略对属性setter和getter的调用,只是直接访问字段(即内联)。当然,允许编译器进行它想要的任何分析并进行进一步的优化。
"否",因为在实践中这通常不是问题。通过在方法中包含访问权限,C#编译器不会优化该字段,并且JIT编译器不太可能这样做(即使方法是内联的......再次,没有保证,但是AFAIK没有进行这样的优化,我认为它不太可能是未来的版本。所以你所剩下的就是内存一致性问题(使用volatile
的另一个原因......即基本上处理在硬件级别执行的优化)。
只要您的代码只能在与Intel x86兼容的硬件上运行,该硬件就会将所有读写操作视为易失性。
但是,其他平台可能会有所不同。 Itanium和ARM是几个具有不同内存模型的常见示例。
就个人而言,我更愿意写下技术细节。编写恰好可行的代码,尽管缺乏保证,但只是要求在将来找到代码因某些神秘原因而停止工作的时间。
所以恕我直言,你真的应该将该字段标记为volatile
。
如果是,当不再需要volatile时,间接有一些限制吗?
没有。如果存在这样的限制,则必须将其记录为有用。在实践中,编译器优化方面的限制实际上只是一个间接级别" (正如你所说)。但是没有多少级别的间接避免了硬件级优化,甚至在编译器优化方面的限制也严格地在实践中#34;。您无法保证编译器永远不会更深入地分析代码并对任何任意深度的调用执行更积极的优化。
更一般地说,我的经验法则是这样的:如果我试图决定是否应该使用我知道的某些特定功能通常用于防止并发相关场景中的错误,我必须要问"我是否真的需要这个功能,或者代码在没有它的情况下能正常工作吗?"然后我可能不太了解该功能对我来说安全避免使用它的方式。
将它视为旧版本的变体"如果您不得不询问它的成本,您将无法负担。"