我如何在Clojure中分裂原子?
我有一个存储在原子中的集合,就像这样我如何在Clojure中分裂原子?,clojure,Clojure,我有一个存储在原子中的集合,就像这样 (def numbers (atom #{1 2 3 4 5})) 使用swap!定期在此集合中自动添加和删除数字。在另一个线程中,我希望有一个函数,可以从集合中提取和删除偶数并返回它们 我可以这样做的一种方法是: (let [{even true odd false} (group-by even? @numbers)] (reset! numbers odd) even) 然而,这不是一个原子操作编号可能会在分组之间通过和重置进行更改。有没有
(def numbers (atom #{1 2 3 4 5}))
使用swap!定期在此集合中自动添加和删除数字代码>。在另一个线程中,我希望有一个函数,可以从集合中提取和删除偶数并返回它们
我可以这样做的一种方法是:
(let [{even true odd false} (group-by even? @numbers)]
(reset! numbers odd)
even)
然而,这不是一个原子操作<代码>编号
可能会在分组之间通过
和重置进行更改代码>。有没有办法以原子方式执行此操作?在这种情况下,维护原子性的惯用方法是使用事务:
(def numbers (ref #{1 2 3 4 5}))
(future (loop []
(Thread/sleep 10)
(dosync
(alter numbers conj (rand-int 1000)))
(recur)))
(dosync
(let [{even true odd false} (group-by even? @numbers)]
(ref-set numbers odd)
even))
有关详细信息,请参阅:
编辑
如果您真的想使用原子,那么这将起作用:
(def numbers (atom #{1 2 3 4 5}))
(future (loop []
(Thread/sleep 10)
(swap! numbers conj (rand-int 1000)))
(recur))
(loop []
(let [val @numbers
{even true odd false} (group-by even? val)]
(if (compare-and-set! numbers val odd)
even
(recur))))
swap!应用的函数代码>不需要是原子的。您应该可以使用swap代码>
如果您只想保留奇数,而要取回偶数,您可以通过swap代码>仅返回赔率,然后原子的值将更新为该值
剩下的问题是如何找回偶数。有很多方法可以做到这一点,我不知道什么是最好的。也许你可以在你的收藏中使用元数据?大概是这样的:
(def numbers (atom #{1 2 3 4 5}))
(defn atom-splitter [xs]
(let [{even true odd false} (group-by even? xs)]
(with-meta (set odd) {:evens (set even)})))
(swap! numbers atom-splitter)
@numbers
的值就是{1 3 5}
(meta@numbers)
的值是{:evens{24}
自从<代码>交换
返回交换的值,您可以确保返回值上的元数据包含拆分集合的另一半。例如,以下各项是安全的:
(defn split-off-evens [a]
(-> (swap! numbers atom-splitter) meta :evens))
调用(拆分偶数)
返回{2 4}
,如果数字
有其原始值。由于这仍然是对atom的正确使用,您可以假设Clojure处理所有线程安全问题。同时,在2021年(自1.9起),您可以使用交换VAL
以原子方式返回新旧值,因此evens当然只是它们之间的差异:
(apply clojure.set/difference
(swap-vals! numbers
#(into #{} (filter odd? %))))
一般来说,让代码执行逻辑(例如将内容拆分)是不纯净的(任何了解引用类型而不是只处理数据的代码都是不纯净的)是一种味道。我认为这不是惯用的。当需要多个一致更新时,可以使用参照进行协调。然而,在这种情况下,仍然只有一个原子被更新。当您需要读-修改-写语义时,REF也是合适的。然而,它可以用原子来完成。请参阅更新的帖子。是的,refs可以为您提供一种安全的方式来执行读-修改-写操作,但这并不意味着您应该在现有的atom操作可以很好地完成任务时使用它们。我更喜欢你的新解决方案。你基本上只是。我想我可能更喜欢使用集合元数据的方法;使用元数据跳出框框思考的好方法。:)有趣的比较和设置!解决方案我喜欢这样,但是你不能在一个无限循环中结束吗?另外,谢谢,我需要更多地了解裁判。