ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
### Atoms Atoms 提供了一种比使用Refs&STM更简单的更新当个值的方法。它不受事务的影响 有三个函数可以修改一个Atom的值: `reset!` , `compare-and-set!` 和 `swap!` . `reset!` 函数接受两个参数:要设值的Atom以及新值。它设置新的值,而不管你旧的值是什么。看例子: ``` (def my-atom (atom 1)) (reset! my-atom 2) (println @my-atom) ; -> 2 ``` `compare-and-set!` 函数接受三个参数:要被修改的Atom, 上次读取时候的值,新的值。 这个函数在设置新值之前会去读Atom现在的值。如果与上次读的时候的值相等, 那么设置新值并返回true, 否则不设置新值, 返回false。看例子: ``` (def my-atom (atom 1)) (defn update-atom [] (let [curr-val @my-atom] (println "update-atom: curr-val =" curr-val) ; -> 1 (Thread/sleep 50) ; give reset! time to run (println (compare-and-set! my-atom curr-val (inc curr-val))))) ; -> false (let [thread (Thread. #(update-atom))] (.start thread) (Thread/sleep 25) ; give thread time to call update-atom (reset! my-atom 3) ; happens after update-atom binds curr-val (.join thread)) ; wait for thread to finish (println @my-atom) ; -> 3 ``` 为什么最后的结果是 3呢? `update-atom` 被放在一个单独的线程里面,在 `reset!` 函数调用之前执行。所以它获取了atom的初始值1(存到变量curr-val里面去了), 然后它sleep了以让 `reset!` 函数有执行是时间。在那之后,atom的值就变成3了。当 `update-atom` 函数调用 `compare-and-set!` 来给这个值加一的时候, 它发现atom的值已经不是它上次读取的那个值了(1), 所以更新失败, atom的值还是3。 `swap!` 函数接受一个要修改的 Atom, 一个计算Atom新值的函数以及一些额外的参数(如果需要的话)。这个计算Atom新的值的函数会以这个Atom以及一些额外的参数做为输入。swap!函数实际上是对compare-and-set!函数的一个封装,但是有一个显著的不同。 它首先把Atom的当前值存入一个变量,然后调用计算新值的函数来计算新值, 然后再调用compare-and-set!函数来赋值。如果赋值成功的话,那就结束了。如果赋值不成功的话, 那么它会重复这个过程,一直到赋值成功为止。这就是它们的区别:所以上面的代码可以用swap!改写成这样: ``` (def my-atom (atom 1)) (defn update-atom [curr-val] (println "update-atom: curr-val =" curr-val) (Thread/sleep 50) ; give reset! time to run (inc curr-val)) (let [thread (Thread. #(swap! my-atom update-atom))] (.start thread) (Thread/sleep 25) ; give swap! time to call update-atom (reset! my-atom 3) (.join thread)) ; wait for thread to finish (println @my-atom) ; -> 4 ``` 为什么输出变成4了呢?因为swap!会不停的去给curr-val加一直到成功为止。