2024 Clojure 状态调查!中分享您的看法。

欢迎!请查看关于页面了解这个站点是如何运作的更多细节。

+6
Clojure

我们现在遇到了这个问题相当频繁,现在我们相当程度地使用 Clojure 对系统进行扩展后,我觉得数字就是在我们那里起反作用,但是不管怎样这都阻碍了 Clojure 在任何多线程环境中的应用。我已创建了一个最小化示例来重现这个问题。所以问题是:

  • 对此问题有 workaround 吗?(类似于一些动态 var,我没有找到,但可能存在)
  • 为什么这被视为“重锤”?(即通常不需要 requiring,主要是在单线程环境中(比如 repl),而大多数生产环境都会从中受益)
  • 有什么线索能解释为什么会发生这种情况吗?(阅读源代码我不太明白为什么会发生这种情况,可能是某个 ns 的 requiring (因此增加一个 ns),增加所有符号,然后在这里,另一个线程在 requiring 它的时候替换了这个 ns)

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 获取了锁...")
    (apply original-require args)
    (println "my-serialized-require 释放锁...")))

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


如果这不可能,您能编译 Clojure 源代码的修改版本并在您的项目中使用它吗?如果是这样,您可以修改 `require` 的定义使其变为带锁版本。
by
就其价值而言,requiring-resolve 是线程安全(带锁)require 的公共 API,我的理解是有计划通过这种机制使常规 require 在某个时刻(也许是 Clojure 1.11?Alex 可能对这一点有了解)线程安全,但在将这种改变应用到 Clojure 的核心部分之前,还需要进行一些分析和可能的研究。
再次感谢,现在我明白了问题所在,我认为我可以为我们的情况实施一个解决方案,但这并不能解决根问题,我希望能看到对此的长期、一般性解决方案。
再次感谢,现在我明白了问题所在,我认为我可以为我们的情况实施一个 workaround(解释一下记者的烦恼,但是没有解决这个问题,我很希望看到一个长期、一般性的解决方案。
我理解您对长期一般性解决方案的渴望。作为一个感兴趣的志愿者,我回应了您关于 workaround 的部分。我对 Clojure 发布版本的内容没有控制权。官方 Clojure 维护者也会查看这些问题,他们会提供他们认为的长期、一般性解决方案。
非常感谢您!
by
大家好,我只是在重申上述问题 - 是的,这是一个已知问题,并已在Clojure 1.11候选列表中,准备加入新版本。并行(冲突)加载相对较少。`requiring-resolve`是在1.10版本中添加的,用于覆盖最常见的动态加载使用场景(这很容易发生竞争)。

就像Andy上面提到的,clojure.lang.RT/REQUIRE_LOCK故意留下可供需要用户程序参与的锁。这应该仍然被视为实现,并可能在未来被删除,但在此期间,可以像你提到的那些情况下使用建议的解决方案。

预期长期解决方案是使常规`require`更安全,但这需要完成显著的分析和测试。
by
gen-class可能也是另一个常见的用例。如果你从Clojure生成一个类,并且Java在多个线程中创建该类的实例,则很可能生成的类初始化器将以非线程安全的方式在内部要求实现Clojure命名空间。我们目前似乎存在这个问题。
...