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,如果我想在服务器端维护一个将要发送到客户端的图像帧队列,我应该使用什么数据结构 我正在尝试制作一个简单的应用程序,我将向服务器发送帧,然后服务器将它们推送到其他客户端 我应该将此队列作为原子还是作为引用来维护?队列最重要的操作是弹出项目:即从集合中获取和删除项目 由于此操作是复合操作,REF自然更适合以原子方式执行,这是为了防止争用条件(例如,两个线程获得相同的项目) 等效功能也可以用原子来实现 (defn remove-and-get [queue] (let [snapshot @que

如果我想在服务器端维护一个将要发送到客户端的图像帧队列,我应该使用什么数据结构

我正在尝试制作一个简单的应用程序,我将向服务器发送帧,然后服务器将它们推送到其他客户端


我应该将此队列作为原子还是作为引用来维护?

队列最重要的操作是弹出项目:即从集合中获取和删除项目

由于此操作是复合操作,REF自然更适合以原子方式执行,这是为了防止争用条件(例如,两个线程获得相同的项目)

等效功能也可以用原子来实现

(defn remove-and-get [queue]
  (let [snapshot @queue
        i (peek snapshot)]
    (if (compare-and-set! queue snapshot (pop snapshot))
      i
      (recur queue))))

(def q (atom (clojure.lang.PersistentQueue/EMPTY)))

(swap! q conj 42)
(swap! q conj :foo)
(swap! q conj [])

[(seq @q) (remove-and-get q) (seq @q)]

您可以只使用
java.util.concurrent
中的一个队列类。轻松访问Java标准库毕竟是Clojure的优点之一,因此,如果Java已经提供了一些功能,那么您不必自己从Clojure提供的构建块构建所有内容

我建议您从界面的实现中选择一些内容。

您可以尝试,其想法如下:

对于每个客户端,您都有一个代理,只需发送命令将帧传输到客户端。因为代理上的操作是按FIFO顺序执行的(至少只要您只有一个发送线程)


似乎有可能发生危险。

危险!什么不起作用 尽管Clojure为您提供了出色的数据结构和原语,但它不会让您免于编写竞争条件。看看这些不起作用的例子:

;; WILL NOT WORK    

(def queue (atom '(:foo :bar :baz :qux)))

;; the code below is called from threads

;; take the first value of the "queue", derefing it

(let [peeked-val (first @queue)]
  (do-something-with peeked-val)
  ;; update the atom to remove the first value
  ;; DANGER: You've derefed the value above but you're swapping it in a separate step
  ;; (here). Other threads may have read the same value or may have mutated it
  ;; in the meantime!
  (swap! queue rest))
ref
s呢

;; WILL NOT WORK    

(def queue (ref '(:foo :bar :baz :qux)))

;; the code below is called from threads

;; take the first value of the "queue", derefing it, this time in a transaction!

(dosync
  (let [peeked-val (first @queue)]
    (do-something-with peeked-val)
    ;; update the ref to remove the first value in the transaction
    ;; DANGER: Refs give you transactional consistency (i.e. consistency
    ;; between read/write access to other refs) but this doesn't really apply
    ;; here as we only have on ref. Other threads may have read the same value
    ;; or may have mutated it in the meantime!
    (alter queue rest)))
用Clojure解决它 您可以通过Clojure的数据结构和原子来实现这一点。关键是使用
交换VAL
因此您只需触摸atom一次-否则您将遇到竞争条件,因为您有两个操作,如上例所示:取消对atom的引用(获取其值)和交换它(更改其值)

您也可以改为使用PersistentQueue,使用
(clojure.lang.PersistentQueue/EMPTY)
构造它-然后您可以在其上调用
peek
pop
,而不是
first
rest
。别忘了把它放在原子中,就像上面的列表:)

使用Java数据结构
你也可以用一些类似的东西。查看ClojureScript编译器中引入并行构建功能的部分,以获取使用
LinkedBlockingDeque

的示例。我记得Rich Hickey在一次演讲中推荐了这种方法。那么Clojure自己的并发解决方案适合哪种应用程序呢?大多数应用程序都会有一个或多个作业队列需要并行处理。在《Clojure编程》一书中,它们展示了一个基于代理的并行程序的示例,该程序就是这样做的:它使用
LinkedBlockingQueue
跟踪需要处理的工作。当代理处理任务时,某些任务会导致在队列上推送更多新任务。小心!第一个示例不是线程安全的,并且将具有争用条件
ref
保证事务一致性(在一个
dosync
中考虑多个ref),但这仍然允许多个线程在给定的时间点读取相同的值。第二个示例可能会起作用,但有点不理想,因为它可能会运行多次,并且在每次迭代中比较一个(很长)列表是否相等可能不是非常有效。
;; WILL NOT WORK    

(def queue (atom '(:foo :bar :baz :qux)))

;; the code below is called from threads

;; take the first value of the "queue", derefing it

(let [peeked-val (first @queue)]
  (do-something-with peeked-val)
  ;; update the atom to remove the first value
  ;; DANGER: You've derefed the value above but you're swapping it in a separate step
  ;; (here). Other threads may have read the same value or may have mutated it
  ;; in the meantime!
  (swap! queue rest))
;; WILL NOT WORK    

(def queue (ref '(:foo :bar :baz :qux)))

;; the code below is called from threads

;; take the first value of the "queue", derefing it, this time in a transaction!

(dosync
  (let [peeked-val (first @queue)]
    (do-something-with peeked-val)
    ;; update the ref to remove the first value in the transaction
    ;; DANGER: Refs give you transactional consistency (i.e. consistency
    ;; between read/write access to other refs) but this doesn't really apply
    ;; here as we only have on ref. Other threads may have read the same value
    ;; or may have mutated it in the meantime!
    (alter queue rest)))
(def queue (atom '(:foo :bar :baz :qux)))

;; use `swap-vals!` on atoms to get both the old and new values of the atom.
;; perfect if you want to peek (get the first value) while doing a pop (removing
;; the first value from the atom, thereby mutating it)

;; use the code below in threads (or somewhere in core.async)
;; it's somewhat non-idiomatic to use `rest` and `first`, see the text below for
;; other options

(let [[old new] (swap-vals! queue rest)]
  (println "here's the popped value:" (first old)))