Clojure原子中的长时间运行功能

时间:2015-01-08 11:18:09

标签: clojure stm

我有一个函数会加载大量用户(需要一段时间)并将它们存储在一个原子中。我想知道将用户加载到let绑定然后重置原子或者只是在原子重置中加载它们之间是否有任何区别!功能

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

(reset! users (get-users))

2 个答案:

答案 0 :(得分:6)

它们是相同的,这就是为什么

由于reset!是一个函数,对(reset! users (get-users))的调用将与Clojure中的任何其他函数调用一样:调用中的每个S表达式都将被计算,然后作为参数传递给功能。这意味着(get-users)的评估将首先发生,结果将传递给reset!。因此,这将与let形式完全相同。

swap!

形成对比

这些问题发挥作用的地方是swap!。因为您发送swap!要在事务中调用的函数,所以您可以更好地控制长期运行的作业是在事务内部还是外部发生。例如,如果您有函数poll-users-updatesupdate-users-from-poll,则可以将对第一个函数的调用设置为在事务内部或外部发生:

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

此处的第二种形式更有可能必须重新启动,因为更新函数运行需要更长的时间,因此需要更多时间对原子进行其他写操作以强制重启。

相反,第一种形式不太可能强制重试,因此通常是首选。另一方面,如果您的poll-users-updates函数还需要对users数据的当前状态进行操作(例如,要查找最近更新的用户的时间戳,以便进行此操作更有效率,然后第二种方法可能是首选方法,因为它可以确保您在进行民意调查时获得最新值users

重试和副作用

有关STM的重点是您的更新功能可能会被多次调用。说副作用函数是“危险在原子内”可能有点强。他们可能危险,并且最好假设它们是。即使它们不是(例如当效果是idempotent时,意味着你得到同样的事情就像你多次调用一样),最好让它们没有副作用。 Clojure的refs和原子都是如此,它们在冲突的情况下重试。在contast中,代理没有重试语义,因此可以在发送给代理的函数中产生副作用。由于代理会对更新函数进行排队并按顺序运行它们,因此不会发生冲突,因此无需重试。

答案 1 :(得分:3)

使用reset!时没有区别。但是,在使用对atom进行操作的其他函数时应该小心,因为可以多次调用value产生函数。例如,这是swap!的情况。