什么是Clojure易变?

时间:2015-07-08 09:26:54

标签: clojure

最近的Clojure 1.7版本中增加了一个:volatile!

volatile已经在很多语言中使用,包括java,但是Clojure中的语义是什么?

它做什么?什么时候有用?

2 个答案:

答案 0 :(得分:58)

新的波动与真实的变量一样接近" (因为它来自许多其他编程语言)因为它得到了clojure。

来自the announcement

  

有一组新功能(volatile!vswap!vreset!volatile?)来创建和使用易变的"框"在状态传感器中保持状态。挥发物比原子快,但放弃了原子性保证,所以只能用于螺纹隔离。

例如,您可以设置/获取和更新它们,就像使用C中的变量一样。 唯一的补充(以及名称)是the actual java objectvolatile关键字。

这是为了防止JVM进行优化,并确保每次访问JVM时都会读取内存位置。 来自the JIRA ticket

  

Clojure需要更快的Atom变体来管理传感器内部的状态。也就是说,Atoms可以完成这项工作,但它们为传感器提供了一些过多的功能。特别是Atoms的比较和交换语义增加了太多的开销。因此,确定简单的volatile ref类型将确保其值的基本传播到其他线程并从任何其他线程读取最新的写入。虽然更新受竞争条件的限制,但访问权限由JVM保证控制。

     

解决方案概述:在Java中创建一个类似于clojure.lang.Box的具体类型,但volatile内部支持IDeref,但不支持watch等。

这意味着,volatile!仍然可以被多个线程访问(这对于传感器来说是必需的)但是它不允许这些线程同时更改,因为它没有给你原子更新。

a java answer中很好地解释了volatile所做的语义:

  

线程安全有两个方面:(1)执行控制,(2)内存可见性。第一个与控制何时执行代码(包括执行指令的顺序)以及它是否可以并发执行有关,第二个与内存中对已完成内容的影响何时对其他线程可见有关。因为每个CPU在它和主内存之间有多个级别的缓存,所以在不同的CPU或内核上运行的线程可以看到"内存"在任何给定的时刻都不同,因为允许线程获取并处理主存储器的私有副本。

现在让我们看看为什么不使用var-settransients

Volatile vs var-set

Rich Hickey didn't want to give truly mutable variables

  

如果没有可变的本地人,人们就会被迫使用复发功能   循环结构。虽然这起初可能看起来很奇怪,但同样如此   简洁为具有突变的环,并且得到的模式可以是   在Clojure的其他地方重复使用,即复发,减少,改变,通勤等   都(逻辑上)非常相似。   [...]   无论如何,Vars   可在适当时使用。

从而创建with-local-varsvar-set等。 这些问题是他们“真实 varsvar-set的文档字符串告诉您:

  

var必须是线程局部绑定的。

当然,这不是core.async的一个选项,它可能在不同的线程上执行。他们做的所有检查都会慢得多。

为什么不使用瞬变

Transients的相似之处在于它们不允许并发访问和优化变异数据结构。 问题是,瞬态仅适用于实现IEditableCollection的集合。也就是说,他们只是为了避免收集数据结构的昂贵中间表示。还要记住,瞬态不会到位,您仍然需要一些内存位置来存储实际的瞬态。 挥发物通常用于简单地持有旗帜或最后一个元素的值(例如,见partition-by

要点:

Volatile只不过是java的易失性包装器,因此具有完全相同的语义。 不要分享他们。只能非常小心地使用它们。

答案 1 :(得分:19)

挥发物是一种“更快的原子”,没有原子性保证。它们是introduced,因为原子被认为太慢而无法在传感器中保持状态。

  

有一组新功能(volatile!vswap!vreset!volatile?)来创建和使用易失性“方框”来保持状态传感器中的状态。挥发物比原子快,但放弃了原子性保证,因此只应与螺纹隔离一起使用