我正在使用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来获取内容并将其刷新到输出流套接字。
答案 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(以及alter
,commute
,ref-set
,ensure
)。
我还要指出,如果你运行这样的循环:
(doseq [c @clients]
...)
然后,在输入clients
表单时,它始终会循环显示doseq
的值,而不管swap!
Atom的clients
是否swap!
在此期间发生过。并不是说这可能是一个问题,只需记住一些事情。
要记住的另一件事是Clojure的引用类型旨在(1)保存不可变数据,(2)使用纯函数更新(在alter
/ send
/ {{1}中} 和朋友)。将原子放入原子中断(1);这可能不是问题,但是你有责任确保它不是。 (Breaking(2)几乎总是一个问题,除非在调试跟踪等特殊情况下你完全想要在失败的CAS等上再次打印。)