在Clojure中处理同时发生的重复事件

时间:2013-03-28 23:32:59

标签: concurrency clojure event-handling

我正在尝试处理重复事件(MIDI音符开启和音符关闭信号一式三份)。似乎使用代理和锁定工作,除非事件(几乎)同时发生。

以下是一个例子:

(def test-val (agent 0))
(def allow-update (agent true))

(defn increase-val []
  (locking allow-update
    (when @allow-update 
      (send allow-update (fn [_ x] x) false)
      (send test-val + 1)))
  (print @test-val))

(defn run-futures [delay-time]
  (send allow-update  (fn [_ x] x) true)
  (send test-val * 0)
  (dotimes [_ 20] 
    (Thread/sleep delay-time)
    (future (increase-val))))

如果我在调用增加值之间稍微延迟测试它:

(run-futures 2)
;outputs 0111111111111111111 every time, as expected

但是,如果我让所有对增加值的调用同时发生:

(run-futures 0)
;001222222222222222
(run-futures 0)
;000000145555555555
(run-futures 0)
;000000013677777777

似乎锁没有时间开启,因此代理商会因多个期货而增加。

我希望我在这里遗漏一些东西,这样我就可以确保我不会对重复的同步事件采取行动。

谢谢!

1 个答案:

答案 0 :(得分:2)

因为代理是异步且不协调的,所以在您向其发送消息与实际运行已发送的操作之间可能会有延迟。在此延迟期间 对于(send test-val + 1)的大多数或全部调用,可能会在第一个实际运行之前通过锁定部分进行调用,并将@ allow-update设置为false。在test-val状态发生变化之前,您可以在前导0中看到这一点。

原子可能更适合与锁交互,尽管refs可能是协调访问多个身份的正确工具。