【问题标题】:Why does Clojure's atom swap! return the new value?为什么 Clojure 的原子交换!返回新值?
【发布时间】:2014-11-10 06:56:46
【问题描述】:

大多数原子运算符返回交换之前的前一个值,例如 C++ 中的std::atomic::fetch_add。使用 atomic int 作为从 0 开始的全局递增 id 是很自然的。为什么 Clojure 的 atom 返回被交换的值?

(def global-counter (atom 0))
(defn next! [] (dec (swap! global-counter inc)))

有没有更好的方法在 Clojure 中创建从零开始的计数器?

【问题讨论】:

  • (def global-counter (atom -1)) 并摆脱dec 电话?
  • @hsestupin 它不适用于 java 中不存在的 unsigned int :)
  • 好的,请看下面我的回答。 AtomicInteger 比 clojure atom 更快,看起来更惯用,更适合您的情况。
  • 谢谢!我还想了解在 Clojure 中返回先前值的决定背后的原因。

标签: clojure


【解决方案1】:

反问:你知道std::atomic::fetch_add为什么会返回交易前的值吗? (我没有。)

调用swap! 执行事务并返回其结果。在并发场景中,如果它返回交易前的值,那么获得交易结果的唯一确定性方法是重复交易中的应用程序,例如。 G。

(def pre-tx (std-swap! global-counter inc))
(def tx-result (inc pre-tx))

当然,可以为交易前价值弥补一个相反的例子。但是,在大多数情况下(以及您的示例案例 - 见下文),tx-result 是与进一步参考相关的值。这就是为什么swap! 被设计为直接返回它的原因。对于不同的要求,ref 是合适的(除非需要使用atom,在这种情况下,您必须使用compare-and-set! 创建自己的自旋循环)。

在您的示例中,next! 应该在第一次调用时返回 1,只要 counting 是给定的示例,就没有理由返回 dec。计数总是从一开始:如果你数一,你的总计数是1。如果next! 曾经返回0(虚构)last! 将返回-1,这是一个无效的总数。

【讨论】:

    【解决方案2】:

    swap! 允许您对原子应用任意函数,并且您事先不知道结果会是什么。如果swap! 没有给你post 值,那么它的用处就会减少;如果你想知道结果,你必须创建一个事务来取消它(或者有人可以在你取消它之前更新它)。

    【讨论】:

    • swap! 不允许fn 的副作用。如果它只是返回以前的值,我将能够为新值执行(fn old-val)。我知道在这里运行两次fn 不太理想。
    • 这根本不是真的。如果swap! 返回事务前值,您也可以通过调用事务中应用程序来构造结果,如我在示例中所示。这对于对交易后价值感兴趣的人会更有用,因为swap! 对于对交易前价值感兴趣的人很有用,因为有时您无法重新创建交易前价值,而您始终可以创建交易后价值价值。取消引用也与问题无关。
    【解决方案3】:

    您可能希望使用 java.util.concurrent.atomic.AtomicInteger 来创建全局递增 id:

    (def global-counter (AtomicInteger. 0))
    (defn next! [] (.getAndIncrement global-counter))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多