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

欢迎!有关此处的更多信息,请查看关于页面。

+6
Clojure

我们经常遇到这个问题,因为现在我们将一个使用Clojure的系统扩展到了相当大的规模,我想数字只是在与我们作对,但无论如何这都阻碍了Clojure在各种多线程环境中的应用。我已经收集了一个最小 示例以重现此问题。所以,问题是:

  • 是否存在这个问题的解决方案?(类似一些我们设置的动态变量,我没有找到但是可能存在)
  • 为什么这被认为是“重型武器”?(即,require几乎不进行,而且在单线程环境(如repl)中,而大多数生产环境将受益于此)
  • 关于为什么会发生这种情况有什么线索?(阅读源代码我不太明白为什么会发生这种情况,可能是某些ns要求(因此添加了ns),添加了所有符号,然后在另一个线程替换这个ns while requiring it的时候)

1 条回答

+2

存在一个私有函数 serialized-require,其在执行 require 前仅获取全局锁,您可以使用它,或者在您的 Java 代码中在任意位置使用 clojure.core/require 时执行相同操作,即通过在一个对象的单个同步方法中调用所有 require 来实现。

by
感谢您的回答,我知道这在我们的情况下不起作用,因为我们至少有一个类是 AOT 编译的,而 AOT 编译会使用 `loadWithClass`,后者会调用 `require`(来加载 `clj` 文件),而这我们又无法控制。
by
如果您无法改变 AOT 编译的代码,因为这真的不是您能控制的,您可以评估一些如下所示代码,并确保它在可以从多个线程调用 `require` 之前被评估吗?当然, printable 的 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 的基本部分之前,还需要进行一些分析和可能的研究。
...
再次感谢,现在我明白问题所在,我认为可以为我们的情况实施一个解决方案,然而,这并没有解决根本问题,我很希望能看到对这个问题的长期、通用的解决方案。
...
再次感谢,现在我明白问题所在,我认为可以为我们的情况实施一个解决方案,然而,这并没有解决根本问题,我很希望能看到对这个问题的长期、通用的解决方案。
...
我理解你希望有一个长期通用的解决方案。我是一个对此感兴趣的热心志愿者,正在回答你询问解决方案的部分。我对Clojure发布版的内容没有任何控制权。官方Clojure维护者也在阅读这些问题,他们可以提出他们认为的长期通用解决方案。
...
非常感谢您的理解!
...
大家好,只是在重复上面的话 - 当然,这是一个已知的问题,也是 Clojure 1.11 版本中包含在内的候选列表之一。并行加载(冲突)相对较少。`requiring-resolve` 在 1.10 版本中添加,以覆盖最常见动态加载场景(可能会轻松发生竞态 condition)。

正如 Andy 所说,clojure.lang.RT/REQUIRE_LOCK 故意使其可用,这样需要参与此锁的用户程序就可以这样做。这仍然是实现内容,可能在未来被删除,但在同时,所推荐的解决方案可以在您提到的情况下使用。

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