我正在阅读 7 周内的 7 个并发模型
这本书。在其中,哲学家被表示为多个 ref
:
(def philosophers (into [] (repeatedly 5 #(ref :thinking))))
每个哲学家的状态在:thinking
和:eating
之间翻转,使用dosync
事务来确保一致性。
现在我想要一个输出当前状态的线程,这样我就可以确定状态在任何时候都是有效的:
(defn status-thread []
(Thread.
#(while true
(dosync
(println (map (fn [p] @p) philosophers))
(Thread/sleep 100)))))
我们使用多个@
来读取每个哲学家的值。当我们 map
哲学家时,一些 refs 可能会发生变化。它会导致我们打印不一致的状态吗?
我知道 Clojure 使用 MVCC
来实现 STM
,但我不确定我是否正确应用了它。
我的交易包含副作用,通常它们不应出现在交易中。但在这种情况下,交易总是会成功,副作用应该只发生一次。可以接受吗?
最佳答案
您的交易并不真的需要副作用,如果您将问题扩大到足够大,我相信交易可能会因为缺少历史记录而失败,如果有大量写入,则重试副作用继续。我认为这里更合适的方法是将 dosync
拉近。交易应该是一个纯粹的、无副作用的事实调查任务。一旦产生了一个值,您就可以在不影响 STM 的情况下自由地使用它执行副作用。
(defn status-thread []
(-> #(while true
(println (dosync (mapv deref philosophers)))
(Thread/sleep 100))
Thread.
.start)) ;;Threw in starting of the thread for my own testing
这里我想提几点:
@
是deref
fn 的阅读器宏,所以(fn [p] @p)
等同于取消引用
。- 您应该避免事务中的惰性,因为某些惰性值可能会在
dosync
的上下文之外进行评估,或者根本不会进行评估。对于map
ping,这意味着您可以使用例如doall
,或者像这里一样只是热切评估的mapv
变体,它生成一个向量而不是一个序列。
关于clojure - 了解 Clojure 中的 STM 属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38569770/