7

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

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

我应该将此队列维护为原子还是参考?

4

5 回答 5

6

您可以只使用其中一个队列类java.util.concurrent。轻松访问 Java 标准库毕竟是 Clojure 的强项之一,因此如果 Java 已经提供了可以完成这项工作的东西,那么您不必自己从 Clojure 提供的构建块构建所有内容。

我建议从接口BlockingQueue的实现中选择一些东西。

于 2013-09-13T12:55:09.170 回答
2

似乎是core.async的一种可能性。

于 2013-09-13T15:31:46.157 回答
2

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

由于这个操作是复合的,refs 更自然地适合以原子方式执行它,这是 - 防止竞争条件(例如,两个线程获得相同的项目)。

(defn remove-and-get [queue]
  (dosync
   (let [i (peek @queue)]
     (alter queue pop)
     i)))

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

(dosync
 (commute q conj 42)
 (commute q conj :foo)
 (commute q conj []))

[(seq @q) (remove-and-get q) (seq @q)]
;; evals to [(42 :foo []) 42 (:foo [])]

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

(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)]
于 2013-09-13T12:37:28.267 回答
2

危险!什么行不通

尽管 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))

s呢ref

;; 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 的数据结构和原子来完成此操作。关键是使用swap-vals!,所以你只接触一次原子 - 否则你会遇到竞争条件,因为你有两个操作,如上例所示:解除原子(获取它的值)和交换它(改变它的值)。

(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)))

您也可以改用 PersistentQueue,用(clojure.lang.PersistentQueue/EMPTY)- 构造它,然后您可以调用peekandpop代替firstand rest。不要忘记把它放在一个原子中,就像上面的列表一样:)

使用 Java 数据结构

您还可以使用类似java.util.concurrent.LinkedBlockingDeque的东西。查看这个为 ClojureScript 编译器引入并行构建功能的提交,以获取使用LinkedBlockingDeque.

于 2018-12-21T21:28:57.290 回答
0

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

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

 (send-off client transmit-frame frame)
于 2013-09-13T14:34:05.197 回答