4

我在clojure.org/refs上读到

从事务的起点(它的“读取点”)开始,所有对 Refs 的读取都将看到“Ref world”的一致快照。事务将看到它所做的任何更改。这称为交易中价值。

在 wikipedia上还有一个指向Snapshot Isolation的链接,这意味着一旦事务开始,读取任意数量的 ref 将彼此一致。

我做了一个测试用例...

(def r1 (ref 0))
(def r2 (ref 0))

(defn delay-then-inc-ref [id ref delay]
  (.start 
    (Thread. 
        #((println id " start")
          (Thread/sleep delay)
          (dosync
             (alter ref inc))
             (println id " end")))))

(defn deref-delay-deref [ref1 ref2 delay]
    (.start 
       (Thread. 
          #((println "S start")
            (dosync 
              (let [a @ref2]
                 (Thread/sleep delay)
                 (println "S r1=" @ref1))) ; @ref1 consistent with @ref2 ?
                 (println "S end")))))

*clojure-version*
;=> {:major 1, :minor 3, :incremental 0, :qualifier nil}
(deref-delay-deref r1 r2 2000) 
(delay-then-inc-ref "1" r1 500)
(delay-then-inc-ref "2" r1 1000)
(delay-then-inc-ref "3" r1 1500)

输出是:

S start
1 start
2 start
3 start
1 end
2 end
3 end
r1 = 3
S end
nil

r1 = 3而不是的值r1 = 0表明,在deref-delay-derefref1 的 deref 中,在三个事务发生sleep选择 r1 的值。delay-then-inc-ref

请注意,我知道ensure防止在特定事务期间其他事务对 refs 进行更新,但我认为这不适用于此处。ref1只要我看到与我的交易开始一致的值,我不在乎是否发生变化。

这种行为如何符合上述参考文档?

4

1 回答 1

1

事实证明,如果 ref 有一些历史记录,它的行为与我预期的一样,因此将 ref 声明更改为添加 a :min-history,然后如图所示设置两个 refs,似乎可以使它工作....

(def r1 (ref 0 :min-history 5))
(def r2 (ref 0 :min-history 5))

(dosync
 (ref-set r1 0)
 (ref-set r2 0))

然后输出是:

S start
1 start
1 end
2 start
2 end
3 start
3 end
S r1= 0
S end
nil

读到这里,很清楚发生了什么。读取事务正在重新启动,因为在事务开始之前的 ref 历史记录中没有条目。为了确认,我添加了更多日志记录:

(defn deref-delay-deref [ref1 ref2 delay]
    (.start 
       (Thread. 
          #((println "S start")
            (dosync
              (println "transaction starting")
              (let [a @ref2]
                 (Thread/sleep delay)
                 (println "S r1=" @ref1))) ; should be consistent with @ref2
            (println "S end")))))

没有历史模型的输出:

S start
transaction starting
1 start
2 start
3 start
1 end
2 end
3 end
transaction starting
S r1= 3
S end

并使用历史模型:

S start
transaction starting
1 start
2 start
3 start
1 end
2 end
3 end
S r1= 0
S end
nil

更新:事实证明,由于测试用例的人为性质,我上面的回答让人分心。在现实世界的使用中,事务是否重新启动并不重要,因为必须编写事务以使其可重新启动。运行时不保证只读事务是否会在存在/不存在历史记录的情况下完成。相反,它可以做任何必要的事情来完成交易世界,并且必须在编写交易代码时牢记这一点。更详细的讨论在这里

我将以上内容留作参考。

于 2012-04-16T17:42:25.237 回答