Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure 有资格的生产者和消费者_Clojure - Fatal编程技术网

Clojure 有资格的生产者和消费者

Clojure 有资格的生产者和消费者,clojure,Clojure,我是clojure的新手,正在努力理解如何正确使用它的并发特性,因此任何批评/建议都将不胜感激。 因此,我试图用clojure编写一个小型测试程序,其工作原理如下: 有5个生产者和2个消费者 生产者等待随机时间,然后将数字推送到共享队列上 消费者应该在队列非空时立即从队列中提取一个数字,然后短时间睡眠以模拟工作 当队列为空时,使用者应阻塞 当队列中有超过4个项目时,生产者应该阻止它,以防止它变得庞大 以下是我对上述每一步的计划: 生产者和消费者将是不真正关心他们国家的代理人(只是零价值或其他)

我是clojure的新手,正在努力理解如何正确使用它的并发特性,因此任何批评/建议都将不胜感激。 因此,我试图用clojure编写一个小型测试程序,其工作原理如下:

  • 有5个生产者和2个消费者
  • 生产者等待随机时间,然后将数字推送到共享队列上
  • 消费者应该在队列非空时立即从队列中提取一个数字,然后短时间睡眠以模拟工作
  • 当队列为空时,使用者应阻塞
  • 当队列中有超过4个项目时,生产者应该阻止它,以防止它变得庞大
  • 以下是我对上述每一步的计划:

  • 生产者和消费者将是不真正关心他们国家的代理人(只是零价值或其他);我只是使用代理在某个时候发送一个“消费者”或“生产者”函数。然后共享队列将是(def queue(ref[])。也许这应该是一个原子

  • 在“producer”代理函数中,只需(Thread/sleep(rand int 1000))和(dosync(alter queue conj(rand int 100)),即可推送到队列上
  • 我正在考虑让消费者代理使用addwatcher监视队列中的更改。但对此不确定。它会在任何更改时唤醒消费者,即使更改来自消费者完成某项操作(可能使其变为空)。也许在watcher函数中检查这一点就足够了。我看到的另一个问题是,如果所有消费者都很忙,那么当生产者向队列中添加新内容时会发生什么?观看的事件是在某个消费者代理上排队,还是消失了
  • 见上文
  • 我真的不知道怎么做。我听说clojure的seque可能有用,但我找不到足够的文档说明如何使用它,而且我的初始测试似乎不起作用(很抱歉,我没有代码了)

  • 以下是我对它的看法。我强调只使用Clojure数据结构来了解这一点。注意,从Java工具箱中获取一个阻塞队列并在这里使用它是非常常见和惯用的;我认为代码很容易适应。更新:我确实将它改编成了
    java.util.concurrent.LinkedBlockingQueue
    ,见下文

    clojure.lang.PersistentQueue 调用
    (pro-con)
    开始测试运行;然后查看
    output
    的内容,查看是否发生了任何事情,
    队列长度
    查看它们是否保持在给定范围内

    更新:为了解释为什么我觉得有必要在下面使用
    sure
    (我在IRC上被问及这一点),这是为了防止写歪斜(请参阅Wikipedia文章中的定义)。如果我将
    @queue
    替换为
    (确保队列)
    ,则两个或多个生产者可以检查队列的长度,发现其小于4,然后在队列上放置其他项目,并可能使队列的总长度超过4,从而打破限制。类似地,两个执行
    @queue
    的消费者可以接受相同的项目进行处理,然后从队列中弹出两个项目<代码>确保防止发生上述任一情况

    (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
    的版本——它实际上比“纯Clojure”版本更干净!而从基于
    clojure.lang.PersistentQueue
    的原始队列到正在处理的队列的更改都是完全本地的。这只是为了说明Clojure的Java互操作到底有多棒。:-)注意:java队列不能正确处理nil值,因此必须用特殊值替换它们。请看cgrand:谢谢你的提醒!我想这也意味着担心
    nil
    不是不使用
    .poll
    的理由。(并不是说其他解决方案在某些情况下不会更合适,比如坚持承诺,并在最后用
    future cancel
    杀死承诺。)出于好奇,对于clojure版本,队列是否需要保留在ref中?因为您不将队列的更新与其他任何内容合并,所以atom不足以实现这样的目的吗?
    (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))