2

在我的大多数 clojure 程序中......以及我看到的许多其他 clojure 程序中,原子中有某种全局变量:

(def *program-state* 
   (atom {:name  "Program"
          :var1  1
          :var2  "Another value"}))

代码中偶尔会提到这种状态。

(defn program-name []
    (:name @*program-state*))

阅读这篇文章http://misko.hevery.com/2008/07/24/how-to-write-3v1l-untestable-code/让我重新思考全局状态,但不知何故,即使我完全同意这篇文章,我认为在 atom 中使用 hash-map 是可以的,因为它提供了一个通用接口来操作全局状态数据(类似于使用不同的数据库来存储数据)。

我想对这个问题有一些其他的想法。

4

3 回答 3

2

我认为拥有一个偶尔以交换方式更新的全局状态很好。当您开始拥有两个需要更新的全局状态并且线程开始使用它们进行通信时,我开始担心。

  • 维护当前全球用户的数量很好:
    • 任何线程都可以在任何时候增加或减少这个,而不会伤害另一个
    • 如果它从你的线程下发生变化,则不会爆炸。
  • 维护日志目录是有问题的:
    • 当它发生变化时,所有线程都会停止写入旧线程吗?
    • 如果两个线程发生变化,它们会收敛。
  • 将其用作消息队列更加可疑:
于 2012-10-29T23:16:15.293 回答
2

这种事情可以,但它也经常是一种设计气味,所以我会谨慎对待。

需要考虑的事情:

  • 一致性- 代码的一部分可以更改程序名称吗?如果是这样,那么program-name从其他线程的角度来看,该函数的行为将不一致。不好!
  • 可测试性——这很容易测试吗?正在更改程序名称的测试套件的一部分可以安全地与另一个正在读取名称的测试同时运行吗?
  • 多个实例- 您是否有应用程序的两个不同部分期望同时使用不同的程序名称?如果是这样,这强烈暗示您的可变状态不应该是全局的。

要考虑的替代方案:

  • 使用ref 而不是 atom,您至少可以确保事务中可变状态的一致性
  • 使用绑定,您可以将可变性限制在每个线程的基础上。这解决了大多数并发问题,并且当您的全局变量像一组线程本地配置参数一样使用时会很有帮助。
  • 尽可能使用不可变的全局状态。它真的需要可变吗?
于 2012-10-30T06:07:54.063 回答
1

我认为拥有这样一个全局状态很好(在许多情况下它是必需的)但我会小心我的应用程序的核心逻辑具有将状态作为参数并返回更新状态而不是直接返回更新状态的函数访问全局状态。基本上我宁愿从几组函数中对全局状态进行受控访问,我程序中的所有其他内容都应该使用这些方法集来访问状态,因为这将允许我抽象出状态实现,即最初我可以开始使用内存中的原子,然后可能会移动到某个持久存储。

于 2012-10-30T04:15:36.310 回答