4

我在代理和发送函数中遇到了一些非常奇怪的行为。我已将行为归结为少量代码。

这段代码:

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

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

谢谢。

4

3 回答 3

3

问题是您deref在动作之外运行代理,因此当将代理的当前值作为参数传递给 时send,排队等待代理的所有动作都会收到相同的值(即nil)。

您可以使用update-in来避免必须deref输入值:

(defn weird-behavior
  []
  (let [my-agent (agent {:entries nil})]
    (doseq [entry [1 2 3]]
      (send my-agent update-in [:entries] conj entry))
    (Thread/sleep 100) ;; Allow time for the sends to finish
    (println my-agent)))

;= #<Agent@1a69a9c: {:entries (3 2 1)}>
于 2013-07-10T21:27:56.880 回答
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)))
于 2013-07-10T21:34:33.307 回答
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)}>  

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

于 2013-07-10T21:39:36.423 回答