在单线程程序中将:volatile-mutable
限定符与deftype
一起使用是否安全?这是this question,this one和this one的后续行动。 (这是一个Clojure问题,但我添加了#34; Java"标签,因为Java程序员也可能对它有洞察力。)
我发现,通过使用:volatile-mutable
中的deftype
字段而不是原子,我可以在我正在制作的程序中获得显着的性能提升,但我&#39因为deftype
的文档字符串说:
请注意,可变字段非常难以使用 正确地,并且仅用于促进更高层的建设 Clojure中的级别构造,例如Clojure的引用类型 本身。它们仅供专家使用 - 如果语义和含义 of:volatile-mutable或:unsynchronized-mutable不是立即的 很明显,你不应该使用它们。
事实上,:volatile-mutable
的语义和含义对我来说并非 。
然而,Emerick,Carper和Grand的 Clojure Programming 第6章说:
"挥发性"这里的含义与volatile中的volatile字段修饰符相同 Java:读取和写入都是原子的,必须在中执行 程序顺序;即,它们不能由JIT编译器重新排序或 由CPU。因此,挥发性物质并不令人惊讶,而且是线程安全的 - 但是 不协调,仍然完全对竞争条件开放。
这似乎意味着只要访问单个volatile-mutable deftype
字段都发生在一个线程中,就没有什么特别值得担心的了。 (没有什么特殊,因为如果我可能使用延迟序列,我仍然要小心处理状态。)因此,如果没有任何东西将并行性引入我的Clojure程序,那么应该没有特别的危险将deftype
与:volatile-mutable
一起使用。
这是对的吗?我不明白有什么危险?
答案 0 :(得分:3)
这是正确的,它是安全的。您只需确保您的上下文实际上是单线程的。有时保证这一点并不容易。
在单线程上下文中使用volatile可变(或只是可变)字段时,线程安全性或原子性没有风险,因为只有#c>一个线程因此两个线程不可能同时向该字段写入新值,或者一个线程根据过时的值写入新值。
正如其他人在评论中指出的那样,您可能只想使用:unsynchronized-mutable
字段来避免volatile引入的成本。这个成本来自于每个写入必须提交到主内存而不是线程本地内存。有关详细信息,请参阅this answer。
同时,在单线程上下文中使用volatile,你什么也得不到,因为没有机会让一个线程写一个新的值,而不会被看到""通过其他线程读取相同的字段。 这就是volatile的用途,但它在单线程环境中无关紧要。
另请注意,clojure 1.7引入了volatile!
旨在提供一个用于管理状态"的易失性框。作为更快的替代品
atom ,具有类似的接口,但没有它的比较和交换语义。使用它时唯一的区别是您拨打vswap!
和vreset!
而不是swap!
和reset!
。我会用它代替
如果我需要一个易失性,请使用^:volatile-mutable
进行deftype。