我遇到了代理和发送功能的一些非常奇怪的行为。我把行为归结为少量代码。
此代码:
(defn weird-behavior
[]
(let [my-agent (agent {:entries nil})]
(doseq [entry [1 2 3]]
(send my-agent assoc :entries (conj (@my-agent :entries) entry)))
(Thread/sleep 100) ;; Allow time for the sends to finish
(println my-agent)))
输出:
#<Agent@222e8b4: {:entries (3)}>
但是,如果我在每次调用之间给它10毫秒,就像这样:
(defn weird-behavior
[]
(let [my-agent (agent {:entries nil})]
(doseq [entry [1 2 3]]
(send my-agent assoc :entries (conj (@my-agent :entries) entry))
(Thread/sleep 10)))
(Thread/sleep 100) ;; Allow time for the sends to finish
(println my-agent)))
输出符合预期:
#<Agent@6211e63b: {:entries (3 2 1)}>
为什么会这样?如何将多个项目连续发送到代理?
感谢。
答案 0 :(得分:3)
问题是你是deref
在代理之外传递代理,所以当将代理的当前值作为参数传递给send
时,排队等待代理的所有操作都会收到相同的值(即{ {1}})。
您可以使用nil
来避免update-in
值:
deref
答案 1 :(得分:1)
send
的调度操作是代理的当前状态的函数。这可能有助于明确这一点,因此您不会尝试自己取消引用它。
(defn weird-behavior []
(let [my-agent (agent {:entries nil})]
(doseq [entry [1 2 3]]
(send my-agent
(fn [state]
(merge-with conj state {:entries entry}))))
(await my-agent)
(println my-agent)))
答案 2 :(得分:1)
通过让您发送给代理的功能打印出要附加的值,可以说明问题。如果不是只调用assoc
而是用println组合它,那么它打印出值,那么你可以清楚地看到有和没有睡眠的函数之间的区别:
(let [my-agent (agent {:entries nil})]
(doseq [entry [1 2 3]]
(send my-agent (comp #(do (println "adding entry" %) %)
assoc)
:entries (conj (@my-agent :entries) entry)))
(Thread/sleep 100) ;; Allow time for the sends to finish
(println my-agent))
adding entry {:entries (1)}
adding entry {:entries (2)}
adding entry {:entries (3)}
#<Agent@75bee6fc: {:entries (3)}>
与睡眠版本对比:
(let [my-agent (agent {:entries nil})]
(doseq [entry [1 2 3]]
(send my-agent (comp #(do (println "adding entry" %) %)
assoc)
:entries (conj (@my-agent :entries) entry))
(Thread/sleep 10))
(Thread/sleep 100) ;; Allow time for the sends to finish
(println my-agent))
adding entry {:entries (1)}
adding entry {:entries (2 1)}
adding entry {:entries (3 2 1)}
#<Agent@1c36ee92: {:entries (3 2 1)}>
这清楚地表明所有三个调用都在读取代理的状态,然后这三个调用都是相互独立地添加到它们读取的值。