单线程上下文中volatile-mutable字段的风险?

Mar*_*ars 6 java clojure thread-safety clojure-java-interop

在单线程程序中使用:volatile-mutable限定符是否安全deftype?这是一个跟进这个问题,这一个这一个.(这是一个Clojure问题,但我添加了"Java"标签,因为Java程序员也可能对它有洞察力.)

我发现在一个程序中我可以通过使用:volatile-mutable字段deftype而不是原子来获得显着的性能提升,但我很担心,因为文档字符串deftype说:

请注意,可变字段非常难以正确使用,并且只是为了便于在Clojure本身中构建更高级别的构造,例如Clojure的引用类型.它们仅供专家使用 - 如果:volatile-mutable或:unynchronized-mutable的语义和含义对您来说不是很明显,那么您就不应该使用它们.

事实上,语义和影响:volatile-mutable不会马上对我明显.

然而,Emerick,Carper和Grand 的Clojure Programming第6章说:

这里的"Volatile"与Java中的volatile字段修饰符含义相同:读取和写入都是原子的,必须按程序顺序执行; 即,它们不能由JIT编译器或CPU重新排序.因此,挥发性物质并不令人惊讶,并且不会引起线程安全 - 但不协调,仍然完全对种族条件开放.

这似乎暗示只要访问单个volatile-mutable deftype字段都发生在一个线程中,就没有什么特别值得担心的了.(没有什么特别的,因为我还是要小心,我是如何处理的状态,如果我可能会使用懒惰序列.)因此,如果没有引入并行到我的Clojure程序,应该使用没有特殊的危险deftype:volatile-mutable.

那是对的吗?我不明白有什么危险?

nbe*_*ger 3

没错,这是安全的。您只需确保您的上下文确实是单线程的。有时要保证这一点并不容易。

在单线程上下文中使用易失性可变(或只是可变)字段时,不存在线程安全或原子性风险,因为只有一个线程,因此两个线程不可能向该字段写入新值同时,或者一个线程根据过时的值写入新值。

正如其他人在评论中指出的那样,您可能只想简单地使用一个:unsynchronized-mutable字段来避免 volatile 带来的成本。该成本来自以下事实:每次写入都必须提交到主内存而不是线程本地内存。有关此内容的更多信息,请参阅此答案。

同时,在单线程上下文中使用 volatile 不会带来任何好处,因为一个线程不可能写入一个不会被读取同一字段的其他线程“看到”的新值。这就是 volatile 的用途,但它在单线程上下文中无关紧要。

另请注意,clojure 1.7 引入的volatile!目的是提供一个“用于管理状态的易失性框”,作为atom的更快替代品,具有类似的界面,但没有比较和交换语义。使用它时的唯一区别是您调用vswap!andvreset!而不是swap!and reset!^:volatile-mutable如果我需要 volatile,我会使用它而不是 deftype with 。