【问题标题】:Why can't I print from background threads in Clojure Cider REPL in emacs?为什么我不能从 emacs 中 Clojure Cider REPL 的后台线程打印?
【发布时间】:2014-12-31 20:43:14
【问题描述】:

如果我尝试在我的 emacs cider-repl 中评估以下代码,则会按预期返回 nil,但不会在 repl 缓冲区或控制台中进行任何打印。我怎样才能按预期打印出来?

(dotimes [i 5]                                                                                                                                        
  (.start                                                                                                                                             
   (Thread.                                                                                                                                           
    (fn []                                                                                                                                             
      (Thread/sleep (rand 500))                                                                                                                       
      (println (format "Finished %d on %s" i (Thread/currentThread)))))))
;=> nil

这很好用,但是:

(println (format "Finished 1 on %s" (Thread/currentThread)))
;=> Finished 1 on Thread[nREPL-worker-18,5,main]
----------- mini-buffer -----------------
nil

【问题讨论】:

    标签: emacs clojure stdout read-eval-print-loop cider


    【解决方案1】:

    如果您使用的是 Figwheel,那么在 ring 处理程序中执行 prn/println(实际上类似于上面显示的 Threads 示例)也可以被 Fighweel 本身吞下。检查您的项目的 project.clj(在 :figwheel 映射中查找键 :server-logfile),您可以在其中控制 out 是否应该转到 repl 或日志文件。请注意,这仅适用于您使用无花果的情况,否则打印到 REPL 当然可以正常工作。

    有关详细信息,请参阅我对此问题的回答:Output compojure server print statements into figwheel terminal?

    【讨论】:

      【解决方案2】:

      在repl中执行下面的表达式,那么所有的输出都会在repl中结束:

      (alter-var-root #'*out* (constantly *out*))
      

      原答案:

      https://groups.google.com/d/msg/cider-emacs/bIVBvRnGO-U/nDszDbGoVzgJ

      【讨论】:

        【解决方案3】:

        *out* 是动态变量,用于确定println 和类似函数的输出去向。 线程绑定到某个地方,导致东西被发送回 emacs 以供 cider 显示;如果您启动一个新线程,则该绑定不存在,并且输出到其他地方(可能到在后台启动的 nrepl 服务器 emacs/leiningen 的标准输出)。

        您可以通过几种方式解决此问题。您可以从父线程中捕获*out* 的值,然后将其传递给闭包中的子线程,然后将*out* 重新绑定到它:

        (let [out *out*] 
          (.start (Thread. (fn [] 
                             (binding [*out* out]
                                (println "test"))))))
        

        或者您可以使用future 而不是自己启动线程:Clojure 会自动将相关的线程本地绑定传递给未来启动的新线程。

        【讨论】:

        • 嘿伙计,你能给我看一个使用future的代码示例吗?我在 repl 上玩过它,但我不太明白。
        • (future (println "test"))
        【解决方案4】:

        println 的行为是使用名为*out* 的动态绑定变量作为其输出流。 emacs 动态绑定 *out* 到 repl 缓冲区以获取在 repl 缓冲区中评估的代码,但是如果您创建一个线程,该线程的 *out* 将获得 *out* 的根绑定,在 cider 的情况下不会repl 缓冲区。

        如果您使用cider-jack-in 启动repl,当您查看缓冲区列表时,应该有一个名称类似于*nrepl-server* 的缓冲区,其中包含根*out* 绑定的输出。这是我运行代码后的内容:

        nREPL server started on port 52034 on host 127.0.0.1 - nrepl://127.0.0.1:52034
        Finished 1 on Thread[Thread-9,5,main]
        Finished 0 on Thread[Thread-8,5,main]
        Finished 2 on Thread[Thread-10,5,main]
        Finished 3 on Thread[Thread-11,5,main]
        Finished 4 on Thread[Thread-12,5,main]
        

        如果您没有使用cider-jack-in,输出将打印到您启动 nrepl 进程的终端。

        【讨论】:

        • 非常感谢!这解决了这个谜,你刚刚教了我一些关于 Clojure 和 Emacs 的知识。有什么方法可以重新绑定后台线程的 out 变量,以便也打印到 repl 缓冲区?
        • *out* 可以用binding 设置,就像任何其他动态变量一样。 @amalloy 的回答很好地展示了我认为的那部分。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-10-06
        • 1970-01-01
        • 2015-10-27
        • 1970-01-01
        • 2015-10-04
        • 1970-01-01
        相关资源
        最近更新 更多