使用 atom 和 future 在我的 Clojure 程序中创建竞争条件
Using atom and future is creating race condition in my Clojure program
我有一个用 Clojure 编写的 Web 服务。它实现了一个简单的 GET 方法,该方法 returns 返回一个 JSON 表示路由器当前位置和不同时间计数器的对象。
我的代码有一堆原子来跟踪时间。每个原子代表机器在给定时间可能进行的不同活动。例如:正在校准、空闲、卡住或正在工作:
(def idle-time (atom 0))
(def working-time (atom 0))
(def stuck-time (atom 0))
(def calibration-time (atom 0))
最后我有一个循环,每 15 秒更新一次位置和时间计数器:
(defn update-machine-info []
(let [machine-info (parse-data-files)]
(update-time-counters machine-info)
(reset! new-state (merge machine-info
{:idleCounter @idle-time
:workingCounter @working-time
:stuckCounter @stuck-time
:calibrationCounter @calibration-time}))))
(loop []
(future
(Thread/sleep 15000)
(update-machine-info)
(recur)))
当前此代码遇到竞争条件,这意味着位置和时间计数器未更新。但是,Web 服务仍然响应正确的 JSON 响应,尽管使用的是旧值。
Web 服务正在使用 Cheshire 将地图生成到 JSON,这里是我的 GET 实现:
(defroutes app-routes
(GET "/" [] (resource :available-media-types ["application/json"]
:handle-ok (generate-string (get-machine-information))))
(route/not-found "Not Found"))
我应该使用 refs 而不是 atoms 吗?我是否正确使用未来? (Thread/sleep 15000) 是否导致了问题,因为原子是异步的?
如果您在我的代码中发现明显的错误,请告诉我。
我认为您不能可靠地在未来内重现到未来之外的循环(不完全确定),但为什么不尝试这样的事情呢?
(future
(loop []
(Thread/sleep 15000)
(update-machine-info)
(recur)))
这样 loop/recur 保持在同一个线程中。
除此之外,如果 update-machine-counters 抛出异常,循环可能会停止,您永远不会看到异常,因为 future 永远不会被取消引用。代理 ( http://clojure.org/agents ) 可能更适合这种情况,因为您可以注册一个错误处理程序。
我认为正在发生的事情是,你调用你的期货的过程在你的期货实际执行之前就终止了。对于您所做的事情,期货可能是错误的构造类型。我也不认为你的循环未来记录序列正在按照你的想法行事。
这里有很多猜测,因为不清楚您实际定义和调用代码的确切位置。我认为您可能想使用代理之类的东西,您需要在根进程中设置它,然后在您 return 回复之前在您的处理程序中向他们发送消息。
我有一个用 Clojure 编写的 Web 服务。它实现了一个简单的 GET 方法,该方法 returns 返回一个 JSON 表示路由器当前位置和不同时间计数器的对象。
我的代码有一堆原子来跟踪时间。每个原子代表机器在给定时间可能进行的不同活动。例如:正在校准、空闲、卡住或正在工作:
(def idle-time (atom 0))
(def working-time (atom 0))
(def stuck-time (atom 0))
(def calibration-time (atom 0))
最后我有一个循环,每 15 秒更新一次位置和时间计数器:
(defn update-machine-info []
(let [machine-info (parse-data-files)]
(update-time-counters machine-info)
(reset! new-state (merge machine-info
{:idleCounter @idle-time
:workingCounter @working-time
:stuckCounter @stuck-time
:calibrationCounter @calibration-time}))))
(loop []
(future
(Thread/sleep 15000)
(update-machine-info)
(recur)))
当前此代码遇到竞争条件,这意味着位置和时间计数器未更新。但是,Web 服务仍然响应正确的 JSON 响应,尽管使用的是旧值。
Web 服务正在使用 Cheshire 将地图生成到 JSON,这里是我的 GET 实现:
(defroutes app-routes
(GET "/" [] (resource :available-media-types ["application/json"]
:handle-ok (generate-string (get-machine-information))))
(route/not-found "Not Found"))
我应该使用 refs 而不是 atoms 吗?我是否正确使用未来? (Thread/sleep 15000) 是否导致了问题,因为原子是异步的?
如果您在我的代码中发现明显的错误,请告诉我。
我认为您不能可靠地在未来内重现到未来之外的循环(不完全确定),但为什么不尝试这样的事情呢?
(future
(loop []
(Thread/sleep 15000)
(update-machine-info)
(recur)))
这样 loop/recur 保持在同一个线程中。
除此之外,如果 update-machine-counters 抛出异常,循环可能会停止,您永远不会看到异常,因为 future 永远不会被取消引用。代理 ( http://clojure.org/agents ) 可能更适合这种情况,因为您可以注册一个错误处理程序。
我认为正在发生的事情是,你调用你的期货的过程在你的期货实际执行之前就终止了。对于您所做的事情,期货可能是错误的构造类型。我也不认为你的循环未来记录序列正在按照你的想法行事。
这里有很多猜测,因为不清楚您实际定义和调用代码的确切位置。我认为您可能想使用代理之类的东西,您需要在根进程中设置它,然后在您 return 回复之前在您的处理程序中向他们发送消息。