Clojure原子中的长时间运行函数

Clojure原子中的长时间运行函数,clojure,stm,Clojure,Stm,我有一个函数,可以加载很多用户(这需要一段时间)并将它们存储在atom中。我想知道将用户加载到let绑定中,然后重置atom,或者只是将用户加载到atom重置中,两者之间是否有任何区别!功能 (let [all-users (get-users)] (reset! users all-users)) 或 使用reset时没有区别。但是,在使用对原子进行操作的其他函数时,应该小心,因为值生成函数可能会被多次调用。例如,.它们是一样的,原因如下 因为重置是一个函数,对(reset!user

我有一个函数,可以加载很多用户(这需要一段时间)并将它们存储在atom中。我想知道将用户加载到let绑定中,然后重置atom,或者只是将用户加载到atom重置中,两者之间是否有任何区别!功能

(let [all-users (get-users)]
    (reset! users all-users))


使用
reset时没有区别。但是,在使用对原子进行操作的其他函数时,应该小心,因为值生成函数可能会被多次调用。例如,.

它们是一样的,原因如下 因为<代码>重置
是一个函数,对
(reset!users(get users))
的调用将与Clojure中的任何其他函数调用一样:调用中的每个S表达式都将被求值,然后作为参数传递给函数。这意味着将首先对
(获取用户)
进行评估,并将结果传递给
重置。因此,这将与
let
表单的行为完全相同

交换进行对比
这些问题的作用在于
swap。因为你发送了
交换要在事务内部调用的函数,您可以更好地控制长时间运行的作业是在事务内部还是外部进行。例如,如果有函数
轮询用户更新
从轮询更新用户
,则可以将对第一个函数的调用设置为在事务内部或外部进行:

; outside the transaction
(swap! users update-users-from-poll (poll-users-updates))
; inside the transaction
(swap! users (fn [users] (update-users-from-poll users (poll-users-updates))))
这里的第二种形式更可能需要重新启动,因为update函数需要更长的时间才能运行,从而为其他对atom的写入留出更多的时间来强制重新启动

相比之下,第一种形式不太可能强制重试,因此通常更可取。另一方面,如果您的
轮询用户更新
功能还需要对
用户
数据的当前状态进行操作(例如,查找最近更新的用户的时间戳,以便更有效地进行轮询),则第二种方法可能是首选方法,因为这将确保您在进行投票时拥有
用户的最新值

关于重试和副作用
这突出显示了您的更新函数可能会被多次调用。说副作用函数“在原子内部是危险的”可能有点强。但它们可能很危险,最好假设它们是。即使它们不是(例如,当效果是,意味着你得到相同的东西调用一次,就像你多次调用一样),最好让它们没有副作用。Clojure的refs和atoms都是如此,它们在发生冲突时重试。相反,代理没有重试语义,所以在发送给代理的函数中有副作用是可以的。由于代理将更新函数排队并按顺序运行,因此不会发生冲突,因此无需重试。

有趣的是,副作用函数在atoms中是危险的。
; outside the transaction
(swap! users update-users-from-poll (poll-users-updates))
; inside the transaction
(swap! users (fn [users] (update-users-from-poll users (poll-users-updates))))