请在 2024 状态调查 中分享您的想法!

欢迎!请查看 关于 页面以获取关于此工作方式的更多信息。

+6
Clojure

我们现在经常遇到这个问题,因为我们使用 Clojure 部署了一整个系统的集群,我认为数字就是不按我们的意愿行事,但是无论怎样,这都阻碍了 Clojure 在每个多线程环境中的应用。我整理了一个 最简 示例 来重现问题。所以,问题是:

  • 这个问题有解决方案吗?(类似一些动态 var,我没有找到,但可能存在)
  • 为什么这被认为是“重型武器”?(例如,require 很少进行,通常在单一线程环境中(如 repl),而大多数生产环境都会从中受益)
  • 有什么线索可以解释为什么会发生这种情况?(阅读源代码,我并不完全理解为什么会发生这种情况,可能是某些 ns 要求(因此添加了 ns),添加了所有符号,而在此期间,另一个线程在 require 它时替换了该 ns)
与以下内容的回答相关: 从 Java 调用 require 不是线程安全的

1 个答案

+2

存在一个私有的函数 serialized-require,它在执行 require 之前简单地获得了全局锁,你可以使用它,或者在你的 Java 代码中使用类似的装卸方式来处理任何对 clojure.core/require 的调用,即在一个单一对象的同步方法内部进行所有 require 调用。

by
感谢您的回答,我知道这一点,然而在我们的情况下这不适用 `:(` 因为至少有一个类是 AOT 编译的,它反过来使用 `loadWithClass`,然后调用 `require`(加载 `.clj` 文件),而这我们无法控制。
by
如果您不能改变那个 AOT 编译的代码,因为它真正不在您的控制之下,那么您能控制评估以下代码,并确保它在多个线程从系统启动时调用 `require` 之前得到评估吗?当然,`println` 是可选的。


(def original-require clojure.core/require)

(defn my-serialized-require [& args]
  (locking clojure.lang.RT/REQUIRE_LOCK
    (println "my-serialized-require acquired the lock...")
    (apply original-require args)
    (println "my-serialized-require releasing lock...")))

(alter-var-root #'clojure.core/require (fn [& args] my-serialized-require))


如果这不可能,您是否允许编译 Clojure 源代码的修改版,并在您的项目中使用它?如果可以,您可以修改 `require` 的定义,使其为锁定的版本。
by
顺便说一下,`requiring-resolve` 是线程安全(已锁定)`require` 的公共 API,我的理解是计划在某些时候(也许是在 Clojure 1.11?Alex 可能会有所了解)将常规 `require` 修改为线程安全,但在这之前需要对 Clojure 的基本部分进行一些分析和研究。
by
再次感谢,现在我明白了问题的症结所在,我认为我可以为我们的情况制定一个变通方案,然而,这并不能解决问题根源,我很想看到对其的长远、普遍解决方案。
再次感谢,现在我明白了问题的症结所在,我认为我可以为我们的情况制定一个hack^H^H^H变通方案,然而,这并不能解决问题根源,我很想看到对其的长远、普遍解决方案。
我理解您对一个长期普遍解决方案的渴望。我是一个感兴趣的志愿者,回应您关于变通方案的提问部分。我对Clojure已发布版本的 内容没有控制权。官方Clojure维护人员也阅读这些问题,并且可以用他们认为的长远、普遍解决方案做出回应。
我会感激您的,谢谢!
你好,只是重复上面的话——是的,这是一个已知问题,已被列入Clojure 1.11的待办事项清单(Candidates for inclusion)。并行(冲突)加载相对较少。`requiring-resolve`是在1.10中加入的,以覆盖最常见的动态加载用例(这很容易竞争)。

正如Andy上面所述,clojure.lang.RT/REQUIRE_LOCK是故意保留的,这样需要这些锁的用户程序就可以参与此锁定。这应该仍然被视为实现细节并可能在未来删除,但同时可以用于您所提到的类似情况中的建议变通方案。

预期长期解决方案是使正常的 `require` 更安全,但这需要大量的分析和测试,这些工作尚未完成。
by
gen-class 也可能是一个常见的用例。如果你从 Clojure 生成一个类,并且 Java 在多个线程中创建该类的实例,那么生成的类初始化器很可能以非线程安全的方式暗地里需要实现 Clojure 命名空间。我们目前似乎有这个问题。
...