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

欢迎!请查看关于页面以了解更多关于如何使用本网站的信息。

+6
Clojure

我们在使用Clojure的一个大量部署的系统上遇到了这个问题,现在我们发现这个问题很常见,也许数字只是在背叛我们,但无论如何,这限制了Clojure在所有多线程环境中的使用。我准备了一个最小化 示例来复现这个问题。所以,问题来了,

  • 这个问题有没有解决方案?(类似于某个我们设置的动态变量,我没有找到,但可能有)
  • 为什么这个问题被认为是“重重锤子”?(例如,require很少做,主要是单线程环境(例如repl),而大多数生产环境将从中受益)
  • 为什么会有这种情况?有什么线索?(阅读源代码,我不明白为什么会发生这种情况,可能是因为某些ns require(以及因此添加ns)添加了所有符号,同时,在添加时,另一个线程正在替换那个ns)

1个回答

+2

存在一个私有函数 serialized-require,它在使用 require 之前简单地获取一个全局锁,您可以选择使用它,或者在您的 Java 代码中使用 clojure.core/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
就我所知,require-resolve 是线程安全(受锁)require 的公共 API,我的理解是,计划是在某一点(或许是 Clojure 1.11?Alex可能会对这个问题有更多了解)让常规 require 线程安全,但在这个变化被应用到 Clojure 的基本部分之前,还需要进行一些分析和研究。
再次感谢,现在我明白问题所在后,我认为可以为我们的情况实施一个解决方案,但是这并没有解决根本问题,我希望看到对这个问题的长期、通用的解决方案。
再次感谢,现在我明白问题所在了,我认为可以为我们的情况实施一个解决方案,但是这并没有解决根本问题,我希望看到对这个问题的长期、通用的解决方案。
我理解您需要一个长期通用的解决方案。我是一名感兴趣的志愿者,正在回答您关于解决方案的部分。我对Clojure发布版本的內容没有控制权。官方Clojure维护人员也在阅读这些问题,他们可以提出他们认为长期通用的解决方案。
非常感激,谢谢!
你好,只是重复上面的事情 - 是的,这是一个已知问题,并且已列入Clojure 1.11的候选列表中。并行(冲突)加载相对较少。`requiring-resolve`是在1.10中添加的,以覆盖最常见的动态加载用例(这很容易竞争)。

正如Andy上面提到的那样,`clojure.lang.RT/REQUIRE_LOCK`被故意保留以供用户程序在需要时参与此锁定。这应该仍然被视为实现问题,并且可能在未来被移除,但在同时,在您提到的情况下,可以采用推荐的解决方案。

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