没有“读取”引用的clojure引用的语义是什么?

时间:2014-12-15 03:46:31

标签: concurrency clojure transactions stm

我已阅读this SO questionhttp://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),我做了以下其中一项:

  1. (alter flag (fn [_] true))
  2. (let [ignored @flag] (ref-set flag true))
  3. 我认为这两个都构成了对标志的读取,因此交易必须重试?

1 个答案:

答案 0 :(得分:2)

调用ref-set表示您已在此事务的跟踪引用中包含flag。因此,在某个其他事务中并发写入flag将导致冲突和重试。

两个后续修改标志(通过alterref-set),因此具有相同的结果。这里重要的不是标志的 read ,它是 write 。如果事务包含没有写入的ref的读取,则即使读取ref在并发事务中发生更改,事务也可以成功。但是,ensure可用于在事务的跟踪引用中包含读取(从而导致并发更改失败)。