我有一个存储当前和历史事件计数的原子。
(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!
期间,我认为它们被正确使用了。有什么想法吗?
答案 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)
问题必须出在您尚未发布的代码中:问题中的所有内容看起来都非常正确,并且不会导致您描述的行为。