clojure - 了解 Clojure 中的 STM 属性

标签 clojure stm

我正在阅读 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

这里我想提几点:

  1. @deref fn 的阅读器宏,所以 (fn [p] @p) 等同于 取消引用
  2. 您应该避免事务中的惰性,因为某些惰性值可能会在dosync 的上下文之外进行评估,或者根本不会进行评估。对于 mapping,这意味着您可以使用例如doall,或者像这里一样只是热切评估的 mapv 变体,它生成一个向量而不是一个序列。

关于clojure - 了解 Clojure 中的 STM 属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38569770/

相关文章:

concurrency - 是否应该在事务中读取多个 Clojure 引用以保持一致性?

concurrency - 单个原子与多个引用

clojure - 更新和替换 map 值

loops - 如何将 Common Lisp 代码的循环部分翻译成 Clojure? ... 功能定位

concurrency - 选择正确的 Clojure 引用类型以协调写入/读取

haskell - 如何在 Haskell 中产生并发计算?

Clojure:删除树的所有叶子中的值

data-structures - Clojure 中的多态性

clojure - 在 Clojure 中创建 Java 类的选项

c - C 的 STM 哈希库(glib?)