原子中不明原因的行为

时间:2015-11-21 19:05:30

标签: clojure

我有一个存储当前和历史事件计数的原子。

(def buckets (atom (list)))

它以20个空值开始。

(reset! buckets (apply list (repeat 20 0)))

=> (0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)

每个事件的头桶增加。

(defn- inc-bucket [buckets]
  (conj (rest buckets) (inc (first buckets))))

(defn event-happened [] (swap! buckets inc-bucket))

=> (event-happened)
(1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)

它会每秒移动一次以提供计数历史。

(defn- shift-buckets [buckets]
  (conj (drop-last buckets) 0))

(once-a-second-run (swap! buckets shift-bucket))

=>  (swap! buckets shift-bucket)
(0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)

这一切在大多数时候都很好。但偶尔(比如说每周一次)我发现buckets的长度已经重置为5,而不是正确的值(本例中为20)。

=> @buckets
(0 1 0 0 0)

使用的唯一地点buckets位于这些函数中,并使用deref来观察值。

出了点问题,我无法弄清楚如何或在哪里。 buckets唯一被修改的位置是在上面两个swap!期间,我认为它们被正确使用了。有什么想法吗?

2 个答案:

答案 0 :(得分:3)

我找到了答案。原子中的懒惰和未处理的异常。没有魔力。

user=> (reset! buckets (apply list (repeat 20 0)))
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
user=> (dotimes [_ 99999] (swap! buckets shift-buckets))
nil
user=> @buckets

StackOverflowError   clojure.core/map/fn--4557 (core.clj:2627)

user=> (type @buckets)
clojure.lang.Cons

这已经出现在日志中,但我没有看到它(还有很多其他事情发生)。但这是疯狂的事情:

user=> @buckets ; again
(0 0)

我不知道为什么会给这个价值。这不是因为cons细胞有头部和尾部,因为在生产中是(0 0 0 0 0)。无论哪种方式,它都是未定义的行为,所以没有必要猜测。

要修复它,每次都必须实现清单。

user=> (defn- shift-buckets-nonlazy [buckets]
  #_=>   (apply list (conj (drop-last buckets) 0)))
#'user/shift-buckets-nonlazy
user=> (reset! buckets (apply list (repeat 20 0)))
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
user=> (dotimes [_ 999999] (swap! buckets shift-buckets-nonlazy))
nil
user=> (type @buckets)
clojure.lang.PersistentList
user=> @buckets
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)

答案 1 :(得分:0)

问题必须出在您尚未发布的代码中:问题中的所有内容看起来都非常正确,并且不会导致您描述的行为。