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

欢迎!请查看 关于 页面了解更多关于本站如何运作的信息。

+4
Clojure CLI
已关闭

Hi Clojure 团队,

在 Liftoff,我们是 Clojure 的大用户(250 多个项目,代码库超过 400k LOC)。我们开发了一些工具,可以根据项目代码的静态分析自动生成 projects 的 deps.edn 文件。

我们的某些工具和测试代码会反复调用 clojure.tools.deps.alpha/resolve-deps。从版本 v0.14.1178 开始,我们注意到以下情况

  • resolve-deps 在每次调用时都会创建一个新的 java.util.concurrent.ExecutorService,但在返回前没有关闭它(《代码》(a rel="nofollow" href="https://github.com/clojure/tools.deps.alpha/blob/v0.14.1178/src/main/clojure/clojure/tools/deps/alpha.clj#L477" target="_blank"))。这导致我们的测试代码产生了 20k+ 线程。
  • clojure.tools.deps.alpha.util.concurrent/submit-task 将线程绑定推入一个固定大小的线程池,但不会弹出它们(《代码》(a rel="nofollow" href="https://github.com/clojure/tools.deps.alpha/blob/v0.14.1178/src/main/clojure/clojure/tools/deps/alpha/util/concurrent.clj#L34" target="_blank"))。由于线程被重复使用,帧栈持续增长。

我们内部现在正在使用一个修订版的工具,它无条件地在每个 resolve-deps 调用中关闭 ExecutorService,并弹出每个任务的线程绑定。这解决了我们的问题,但我们不知道这是否是一个正确的解决方案。

Clojure 核心团队如何看待 CLI 工具中的并发?我们能否期待 CLI 工具的未来版本发生变化这种行为?

谢谢!

已关闭,附注:在 Clojure CLI 1.11.1.1347 中发布

2 答案

+2

选中了
 
最佳答案

谢谢!这些问题很重要,tools.deps应该表现得更好。由于很多情况下tools.deps都是单次使用(如CLI),所以在那里并没有太大关系,但我同意这是一个问题。

已记录为 https://clojure.atlassian.net/browse/TDEPS-227,如果您有补丁,我会很高兴考虑。(有关流程,请参阅 https://clojure.org/dev/dev#_becoming_a_contributor

嗨Alex。感谢您的迅速回复。我很乐意贡献补丁,但我不确定如何签署贡献协议。我的雇主对我的贡献有权利,但我没有权限绑定我的雇主。我们该如何进行?
要么是您雇主签署,要么不幸的是,我们无法接受您的贡献。
0

以下是一个提议的补丁

diff --git a/src/main/clojure/clojure/tools/deps/alpha.clj b/src/main/clojure/clojure/tools/deps/alpha.clj
index ec0561f1..70bef534 100644
--- a/src/main/clojure/clojure/tools/deps/alpha.clj
+++ b/src/main/clojure/clojure/tools/deps/alpha.clj
@@ -489,16 +489,20 @@
   {:arglists '([deps-map args-map])}
   ([deps-map args-map]
    (let [{:keys [extra-deps default-deps override-deps threads trace]} args-map
-         n (or threads concurrent/processors)
-         executor (concurrent/new-executor n)
          deps (merge (:deps deps-map) extra-deps)
-         version-map (-> deps
-                       (canonicalize-deps deps-map)
-                       (expand-deps default-deps override-deps deps-map executor trace)
-                       cut-orphans)
-         lib-map (lib-paths version-map)
-         lib-map' (download-libs executor lib-map deps-map)]
-     (with-meta lib-map' (meta version-map))))
+         n (or threads concurrent/processors)
+         executor (concurrent/new-executor n)]
+     (try
+       (let [version-map (-> deps
+                             (canonicalize-deps deps-map)
+                             (expand-deps default-deps override-deps deps-map executor trace)
+                             cut-orphans)
+             lib-map (lib-paths version-map)
+             lib-map' (download-libs executor lib-map deps-map)]
+         (with-meta lib-map' (meta version-map)))
+       (finally
+         ;; Shutdown executor to avoid creating too many threads.
+         (.shutdownNow executor)))))
   ;; deprecated arity, retained for backwards compatibility
   ([deps-map args-map settings]
    (resolve-deps deps-map (merge args-map settings))))
...