Clojure - 连续多个发送的问题

时间:2013-07-10 21:10:19

标签: clojure

我遇到了代理和发送功能的一些非常奇怪的行为。我把行为归结为少量代码。

此代码:

(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)}>

为什么会这样?如何将多个项目连续发送到代理?

感谢。

3 个答案:

答案 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)}>  

这清楚地表明所有三个调用都在读取代理的状态,然后这三个调用都是相互独立地添加到它们读取的值。