【问题标题】:Update clojure code running in a thread更新线程中运行的 clojure 代码
【发布时间】:2014-01-16 18:20:57
【问题描述】:

现在,我需要在 clojure/java 混合系统中重新加载某种代码。

我知道 tools.namespace lib 可以用来刷新 clj 代码。但是当我尝试重新加载线程中使用的代码时,它没有更新。

这是我需要更新的示例 clojure 代码:(只需在大写和小写之间切换。)

(ns com.test.test-util)
(defn get-word [sentence]
    (let [a-word (rand-nth (clojure.string/split sentence #"\s"))
        ;;a-word (clojure.string/upper-case a-word)
        ]
        (println a-word)
        a-word))

我用future启动了一个线程,调用了上面的代码。

在 main 方法中,在我的第一个测试中,我只是创建了一个 future 并在一段时间后刷新代码。但它没有更新。所以,我还测试了刷新后创建另一个线程。但是代码没有更新。

(defn start-job []
  (future (
            (println "start worker job.")
            (while @job-cond
              (do (get-word (rand-nth sentences))
                (Thread/sleep 2000)) )
            (println "return!")
            )))

(defn -main []
  (let [work-job (start-job)]
    (println "modify clj file...")
    (Thread/sleep 10000)
    (swap! job-cond not)
    (future-cancel work-job)
    (swap! job-cond not)

    (refresh)
    (Thread/sleep 3000)

    (let [new-work-job (start-job)]
      (Thread/sleep 10000)
      (swap! job-cond not)
      (future-cancel new-work-job))))

谢谢。

【问题讨论】:

  • 您有多余的括号,保证会在 start-job 定义中导致 NPE。不带引号的括号不会对 clojure 中的代码块进行分组,它们会调用函数或宏调用。 Future 有一个隐式 do 块,因此您不需要将代码块包装在 future 中。 while 块中的 do 也是不需要的,因为 while 也有一个隐式 do。

标签: clojure reload


【解决方案1】:

一般来说,您不希望代码重新加载影响运行时。相反,您应该尝试寻找干净的方法来使用 e 启动和关闭正在运行的应用程序代码。 G。 Stuart Sierras 图书馆 componentjuxt/jig,作者 Malcolm Sparks。然后仅在您的应用程序启动并运行(或至少不是依赖于被重新加载的命名空间的运行代码部分)时重新加载命名空间。

话虽如此,这就是您的示例不起作用的原因:

start-job 在其生命周期内无法自动引用重新加载的get-word 版本,因为它使用的get-work 版本在编译时已解析。所以它仍然会使用旧版本。

使用

显式解析 get-word 的 var
(resolve `get-word)

并将返回的 var 作为函数调用,而不是将来直接使用 get-word

然后会发生什么? 在您进行更改并调用refresh 之后,-main 仍将引用旧版本的start-job(因此,如果您更改它,它将不会在运行时生效)。但是,其中get-word 将按上述方式解决,并且将使用新版本的get-word

简短说明:你不能指望一个正在运行的编译函数会在运行中用重新加载的代码替换自己(无论它是否在单独的线程上运行)。这将是必要的(除非您在其中使用 resolve),但它需要调用重新加载的函数。

【讨论】:

  • 我认为您可以随时重新加载代码,这仅取决于技术。例如,Ring 应用程序中的一个常见习惯用法是传递 '#var 作为处理程序,例如 run-jetty '#app 而不是 run-jetty app。因此,如果您更改 var 引用的函数并重新评估 var,您将即时获得新代码。
  • 您重新评估是正确的。但是,对于重新加载名称空间,它不起作用。重新评估意味着您只需在当前环境中重新评估表单,这意味着将调用 defn,这只会改变 var 的根绑定。如果你传递一个 var 而不是一个函数,它的根绑定将在调用之前被查找。但是,如果您正在重新加载命名空间,则 var(包括其根绑定)将被遗忘(连同包含它的命名空间类),您只能在新加载的命名空间中找到新版本。
  • 谢谢。我正在计划使用 Stuart Sierras 的组件库来重新加载工作人员。并在重置之前刷新代码。
猜你喜欢
  • 2022-11-11
  • 2015-01-25
  • 1970-01-01
  • 1970-01-01
  • 2010-09-26
  • 2021-06-21
  • 1970-01-01
  • 2012-05-21
  • 1970-01-01
相关资源
最近更新 更多