我是clojure的新手,我正试图理解如何正确地使用它的并发特性,因此,任何批评/建议都会受到赞赏。因此,我试图用clojure编写一个小的测试程序,其工作方式如下:
以下是我对上述每一步的计划:
发布于 2010-05-03 18:22:42
这是我对它的看法。我只想使用Clojure数据结构来了解它是如何工作的。请注意,从Java工具箱中获取阻塞队列并在这里使用是非常常见和惯用的;我认为代码很容易修改。更新:实际上,我确实将其改编为java.util.concurrent.LinkedBlockingQueue,见下文。
clojure.lang.PersistentQueue
调用(pro-con)启动测试运行;然后查看output的内容,看看是否发生了任何事情,queue-lengths查看它们是否停留在给定的范围内。
更新:为了解释为什么我觉得需要在下面使用ensure (我在IRC上被问到这个问题),这是为了防止写错误(请参阅维基百科关于快照隔离的文章中的定义)。如果我用@queue代替(ensure queue),那么两个或多个生产者可能会检查队列的长度,发现队列长度小于4,然后在队列中放置额外的项,并可能使队列的总长度超过4,从而打破约束。类似地,两个执行@queue的消费者可以接受相同的项进行处理,然后从队列中弹出两个项。ensure防止这两种情况中的任何一种发生。
(def go-on? (atom true))
(def queue (ref clojure.lang.PersistentQueue/EMPTY))
(def output (ref ()))
(def queue-lengths (ref ()))
(def *max-queue-length* 4)
(defn overseer
([] (overseer 20000))
([timeout]
(Thread/sleep timeout)
(swap! go-on? not)))
(defn queue-length-watch [_ _ _ new-queue-state]
(dosync (alter queue-lengths conj (count new-queue-state))))
(add-watch queue :queue-length-watch queue-length-watch)
(defn producer [tag]
(future
(while @go-on?
(if (dosync (let [l (count (ensure queue))]
(when (< l *max-queue-length*)
(alter queue conj tag)
true)))
(Thread/sleep (rand-int 2000))))))
(defn consumer []
(future
(while @go-on?
(Thread/sleep 100) ; don't look at the queue too often
(when-let [item (dosync (let [item (first (ensure queue))]
(alter queue pop)
item))]
(Thread/sleep (rand-int 500)) ; do stuff
(dosync (alter output conj item)))))) ; and let us know
(defn pro-con []
(reset! go-on? true)
(dorun (map #(%1 %2)
(repeat 5 producer)
(iterate inc 0)))
(dorun (repeatedly 2 consumer))
(overseer))java.util.concurrent.LinkedBlockingQueue
使用LinkedBlockingQueue编写的上述版本。注意代码的总体轮廓是如何基本相同的,有些细节实际上稍微清晰一些。我从这个版本中删除了queue-lengths,因为LBQ为我们处理了这个约束。
(def go-on? (atom true))
(def *max-queue-length* 4)
(def queue (java.util.concurrent.LinkedBlockingQueue. *max-queue-length*))
(def output (ref ()))
(defn overseer
([] (overseer 20000))
([timeout]
(Thread/sleep timeout)
(swap! go-on? not)))
(defn producer [tag]
(future
(while @go-on?
(.put queue tag)
(Thread/sleep (rand-int 2000)))))
(defn consumer []
(future
(while @go-on?
;; I'm using .poll on the next line so as not to block
;; indefinitely if we're done; note that this has the
;; side effect that nulls = nils on the queue will not
;; be handled; there's a number of other ways to go about
;; this if this is a problem, see docs on LinkedBlockingQueue
(when-let [item (.poll queue)]
(Thread/sleep (rand-int 500)) ; do stuff
(dosync (alter output conj item)))))) ; and let us know
(defn pro-con []
(reset! go-on? true)
(dorun (map #(%1 %2)
(repeat 5 producer)
(iterate inc 0)))
(dorun (repeatedly 2 consumer))
(overseer))https://stackoverflow.com/questions/2760017
复制相似问题