在Clojure中删除原子列表中项目的最佳方法

时间:2013-07-19 12:49:14

标签: multithreading clojure atomic

我正在使用server.socket将数据流式传输到多个客户端,server.socket为每个客户端连接使用Threads。 我现在有这样的事情:

(def clients (atom ())) ; connected clients defined globally for that namespace

(swap! clients conj a)  ; adds a client (which is an atom itself as well), this is in a function that is run on the client's thread

;I want to better the process of removing a client!
(dosync (reset! clients (remove #{a} @clients))) ; removes client from list, run in a function on the client's thread

我运行一个遍历每个客户端并抓取内容的函数,它在每个多个客户端线程上处于无限循环中,因此它同时运行:

(doseq [c @clients]
  (print ((deref c) :content))
  (flush))

我在线程中使用Atoms的结论确实让程序顺利运行并且允许非阻塞读取,所以我很高兴这个,除了我觉得重置全局客户端的Atom只是为了我可以删除一个列表中的单个客户端是一个糟糕的举动。有没有更合适的方法来实现这一点使用交换! ?我为clients atom选择了list,因为我在每个连接的客户端上运行doseq来获取内容并将其刷新到输出流套接字。

2 个答案:

答案 0 :(得分:5)

避免在swap!reset!内解析原子。

这里swap!将为您提供所需的信息。它需要一个接收当前值的函数,您可以将其用于更新:

(def clients (atom '(:a :b :c :d)))
(swap! clients (fn [s] (remove #{:a} s)))

您可能习惯于不像上面那样明确地看到swap!的函数参数,因为swap!会将该函数应用于所提供的任何其他参数,因此如果它们的顺序正确,例如如果我们使用set for clients,我们可以

(def clients (atom #{:a :b :c :d}))
(swap! clients disj :a)

答案 1 :(得分:3)

当然,您可以使用swap!,参见A. Webb的回答。

您可能想要考虑是否将您的客户存储在列表中是最佳选择;集合或地图将是更自然的选择(与disj / dissoc一起使用)。 (除非总是有非常少量的客户端,在这种情况下,使用可用的最不复杂的数据结构可能是有意义的。)

此外,dosync在此无效。 dosync适用于Refs(以及altercommuteref-setensure)。

我还要指出,如果你运行这样的循环:

(doseq [c @clients]
   ...)

然后,在输入clients表单时,它始终会循环显示doseq的值,而不管swap! Atom的clients是否swap!在此期间发生过。并不是说这可能是一个问题,只需记住一些事情。

要记住的另一件事是Clojure的引用类型旨在(1)保存不可变数据,(2)使用纯函数更新(在alter / send / {{1}中} 和朋友)。将原子放入原子中断(1);这可能不是问题,但是你有责任确保它不是。 (Breaking(2)几乎总是一个问题,除非在调试跟踪等特殊情况下你完全想要在失败的CAS等上再次打印。)