如果一个线程在 swap! 期间改变了一个原子的值会发生什么?
What happens if a thread changes the value of an atom during `swap!`?
根据官方 Clojure docs:
Since another thread may have changed the value in the intervening time, it [swap!
] may have to
retry, and does so in a spin loop.
这是否意味着如果所讨论的原子从未 returns 到 swap!
读取的值,则执行交换的线程可能会永远卡在 swap!
中?
没有*。当原子重新尝试操作时,它使用“新”值进行比较。
您可以在网上找到许多示例(例如 here and also here),其中人们使用数十到数百个线程来敲打一个原子,并且总是 returns 正确的结果。
* 除非您有无数次来自竞争的“更快”线程的中断。
是的。如果你有一个非常慢的突变与大量快速操作竞争,慢速操作将不得不每次重试,并且直到所有快速操作完成后才会完成。如果快速操作无限期地进行,那么慢速操作将永远不会完成。
例如,尝试:
(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)
永远持续下去。
根据官方 Clojure docs:
Since another thread may have changed the value in the intervening time, it [
swap!
] may have to retry, and does so in a spin loop.
这是否意味着如果所讨论的原子从未 returns 到 swap!
读取的值,则执行交换的线程可能会永远卡在 swap!
中?
没有*。当原子重新尝试操作时,它使用“新”值进行比较。
您可以在网上找到许多示例(例如 here and also here),其中人们使用数十到数百个线程来敲打一个原子,并且总是 returns 正确的结果。
* 除非您有无数次来自竞争的“更快”线程的中断。
是的。如果你有一个非常慢的突变与大量快速操作竞争,慢速操作将不得不每次重试,并且直到所有快速操作完成后才会完成。如果快速操作无限期地进行,那么慢速操作将永远不会完成。
例如,尝试:
(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)
永远持续下去。