大多数原子运算符在交换之前返回先前的值,例如C ++中的std::atomic::fetch_add
。使用原子int作为从0开始的全局增加id是很自然的。为什么Clojure的原子返回交换的值?
(def global-counter (atom 0))
(defn next! [] (dec (swap! global-counter inc)))
有没有更好的方法在Clojure中创建一个从零开始的计数器?
答案 0 :(得分:3)
反问题:你知道为什么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
,只要计数是给定示例,就没有理由dec
。计数始终以一个开头:如果算一,则总计数为1
。如果next!
曾返回0
(虚构)last!
将返回-1
,则总计数无效。
答案 1 :(得分:2)
您可能希望使用java.util.concurrent.atomic.AtomicInteger
来创建全局增加ID:
(def global-counter (AtomicInteger. 0))
(defn next! [] (.getAndIncrement global-counter))
答案 2 :(得分:2)
swap!
允许您将任意函数应用于原子,并且您事先不知道结果是什么。如果swap!
没有给你发布值,那么它就没用了;如果你想知道结果(或者有人可以在你揭穿它之前更新它),你必须创建一个deref它的交易。