我已阅读this SO question和http://clojure.org/refs,但我仍然对确切的ref-set如何工作感到困惑。 (在某种程度上,这两个文件让我相信两件不同的事情......)
假设我在Clojure中有一个如下所示的事务:
(def flag (ref false))
(dosync
(long-computation-that-does-not-read-or-write-flag)
(ref-set flag true))
假设在长计算的中间,其他人修改了标志。当我尝试重新设置标志时,这会导致我的事务重试吗?
我可以想象答案可能是肯定的,因为clojure.org说交易保证"No changes will have been made by any other transactions to any Refs that have been ref-set/altered/ensured by this transaction"
。
但我也可以想象答案是否定的,因为我从未读取标志,而clojure.org页面则表明"All *reads* of Refs will see a consistent snapshot of the 'Ref world' as of the starting point of the transaction"
。这也是链接的SO答案会让我相信的。
接下来:假设代替(ref-set flag true)
,我做了以下其中一项:
(alter flag (fn [_] true))
(let [ignored @flag] (ref-set flag true))
我认为这两个都构成了对标志的读取,因此交易必须重试?
答案 0 :(得分:2)
调用ref-set
表示您已在此事务的跟踪引用中包含flag
。因此,在某个其他事务中并发写入flag
将导致冲突和重试。
两个后续修改标志(通过alter
和ref-set
),因此具有相同的结果。这里重要的不是标志的 read ,它是 write 。如果事务包含没有写入的ref的读取,则即使读取ref在并发事务中发生更改,事务也可以成功。但是,ensure可用于在事务的跟踪引用中包含读取(从而导致并发更改失败)。