【问题标题】:What happens if a thread changes the value of an atom during `swap!`?如果线程在 `swap!` 期间更改原子的值会发生什么?
【发布时间】:2021-06-21 23:34:33
【问题描述】:

根据 Clojure 官方docs

由于另一个线程可能在中间时间更改了该值,因此 [swap!] 可能必须 重试,并在自旋循环中这样做。

这是否意味着如果相关原子永远不会返回到swap! 读取的值,那么执行交换的线程可能会永远卡在swap! 内?

【问题讨论】:

    标签: clojure


    【解决方案1】:

    是的。如果你有一个非常慢的突变与大量的快速操作竞争,那么慢操作每次都必须重试,并且直到所有快速操作都完成后才会完成。如果快速操作无限期进行,那么慢操作将永远无法完成。

    例如,试试:

    (time 
      (let [a (atom 0)] 
        (future (dotimes [_ 1e9] (swap! a inc))) 
        (swap! a (fn [x] (Thread/sleep 1000) (* 2 x)))))
    

    首先,您会看到完成需要很长时间,比一秒钟长得多。这是因为循环外的swap! 在较小的任务全部完成之前无法取得任何进展。您还会看到,您得到的答案正好是 2000000000,这意味着加倍操作肯定是最后发生的,在每个增量之后。如果有更多的增量,它们都将获得“优先级”。

    我还想到了一些可爱的方法,可以让原子永远死锁,而不会占用更多线程!

    一种方法是让线程与自己竞争:

    (let [a (atom 0)]
      (swap! a (fn [x] 
                 (swap! a inc')
                 (inc' x))))
    

    我使用inc',所以它确实是永远的:它不会在Long/MAX_VALUE 之后中断。

    而且这种方式甚至不涉及另一个swap! 操作,更不用说另一个线程了!

    (swap! (atom (repeat 1)) rest)
    

    这里的问题是比较和交换中的.equals 比较永远不会终止,因为(repeat 1) 会一直持续下去。

    【讨论】:

      【解决方案2】:

      没有*。当原子重试操作时,它使用“新”值进行比较。

      您可以在网上找到许多示例(例如 herealso here),其中人们使用了数十到数百个线程来敲击一个原子,并且总是返回正确的结果。


      * 除非您有无数次来自竞争“更快”线程的中断。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-01-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-04
        • 2016-07-25
        • 1970-01-01
        相关资源
        最近更新 更多