为什么我不能从 emacs 中的 Clojure Cider REPL 中的后台线程打印?



如果我试图在我的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

println的行为是使用一个名为*out*的动态绑定var作为其输出流。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进程的终端。

*out*是决定println和类似函数输出去向的动态变量。它被线程绑定到某个地方,导致内容被发送回emacs以供cider显示;如果启动一个新线程,则该绑定不存在,输出将转到其他位置(可能是后台启动的nrepl服务器emacs/leiningen的stdout)。

你可以用几种方法来解决这个问题。您可以从父线程捕获*out*的值,然后在闭包中将其传递给子线程,并将*out*重新绑定到它:

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

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

在repl中执行以下表达式,然后所有输出都将出现在repl:中

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

最初的答案:

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

如果您使用的是Fighwheel,那么在环形处理程序中执行prn/println(实际上类似于上面显示的Threads示例)也可以被Fighwheel本身所接受。检查您的项目的project.clj(在:figwheel映射中查找key:server日志文件),在那里您可以控制out是否应该转到repl或日志文件。请注意,这只适用于使用figwheel的情况,否则打印到REPL当然可以。

有关更多详细信息,请参阅我对此问题的回答:是否将compjure服务器打印语句输出到figwheel终端?

最新更新