单线程上下文中易失可变字段的风险?

Risks of volatile-mutable fields in single-threaded contexts?

在单线程程序中使用 :volatile-mutable 限定符和 deftype 是否安全?这是 this question, this one, and this one 的跟进。 (这是一个 Clojure 问题,但我添加了 "Java" 标记,因为 Java 程序员也可能对此有见解。)

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

Note well that mutable fields are extremely difficult to use correctly, and are present only to facilitate the building of higher level constructs, such as Clojure's reference types, in Clojure itself. They are for experts only - if the semantics and implications of :volatile-mutable or :unsynchronized-mutable are not immediately apparent to you, you should not be using them.

事实上,:volatile-mutable 的语义和含义对我来说不是

然而,Emerick、Carper 和 Grand 的 Clojure 编程 的第 6 章说:

"Volatile" here has the same meaning as the volatile field modifier in Java: reads and writes are atomic and must be executed in program order; i.e., they cannot be reordered by the JIT compiler or by the CPU. Volatiles are thus unsurprising and thread-safe — but uncoordinated and still entirely open to race conditions.

这似乎意味着只要对单个易失可变 deftype 字段的访问全部发生在单个线程中,就没有什么特别需要担心的。 (没有什么 特殊 ,因为如果我可能使用惰性序列,我仍然必须小心处理状态的方式。)因此,如果没有任何东西将并行性引入我的 Clojure 程序,那么应该没有将 deftype:volatile-mutable.

一起使用的特殊危险

对吗?我不了解哪些危险?

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

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

正如其他人在评论中指出的那样,您可能只想使用 :unsynchronized-mutable 字段来避免 volatile 引入的成本。该成本来自这样一个事实,即每次写入都必须 提交到主内存 而不是线程本地内存。有关详细信息,请参阅 this answer

与此同时,在单线程上下文中使用 volatile 不会有任何好处,因为不可能让一个线程写入新值,而其他线程读取同一字段不会 "seen" . 这就是 volatile 的用途,但它与单线程上下文无关。

另请注意,clojure 1.7 引入了 volatile! 旨在提供 "volatile box for managing state" 作为 更快的替代方案 atom,具有相似的界面,但没有比较和交换语义。使用它时唯一的区别是你调用 vswap!vreset! 而不是 swap!reset!。我会用那个代替 如果我需要 volatile.

,请使用 ^:volatile-mutable deftype