我正在开发 Clojure/Jetty Web 服务。我有一个特殊的 url,我希望一次只处理一个请求。如果请求了 url,在它返回之前,又请求了 url,我想立即返回。所以在更多 core.clj 中,我定义了我的路线,我有这样的东西:
(def work-in-progress (ref false))
然后过一段时间
(compojure.core/GET "/myapp/internal/do-work" []
(if @work-in-progress
"Work in Progress please try again later"
(do
(dosync
(ref-set work-in-progress true))
(do-the-work)
(dosync
(ref-set rebuild-in-progress false))
"Job completed Successfully")))
我已经在本地 Jetty 服务器上尝试过这个,但我似乎能够点击 url 两次并使工作翻倍。在线程 Web 服务器环境中,在 Clojure 中实现这一点的好模式/方法是什么?
最佳答案
想象以下 race condition对于问题中提出的解决方案。
@work-in-progress
是 false
,所以它进入 do
表达。但是,在它设法设置 work-in-progress
的值之前至 true
... @work-in-progress
是假的,所以它进入 do
表达。 现在有两个线程正在执行
(do-the-work)
同时。那不是我们想要的。为防止出现此问题,请检查并在
dosync
中设置 ref 的值。交易。(compojure.core/GET "/myapp/internal/do-work" []
(if (dosync
(when-not @work-in-progress
(ref-set work-in-progress true)))
(try
(do-the-work)
"Job completed Successfully"
(finally
(dosync
(ref-set work-in-progress false))))
"Work in Progress please try again later"))
在这种情况下,您可能会发现另一个有用的抽象是原子和
compare-and-set!
.(def work-in-progress (atom false))
(compojure.core/GET "/myapp/internal/do-work" []
(if (compare-and-set! work-in-progress false true)
(try
(do-the-work)
"Job completed Successfully"
(finally
(reset! work-in-progress false)))
"Work in Progress please try again later"))
关于Clojure/jetty : Force URL to only be Hit Once at a Time,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16947882/