使用swank/slime理解Clojure中的输出

6
当我在emacs的Swank repl中运行Clojure代码时,主线程将使用printf向repl打印出消息。但是,如果我运行代理或显式创建其他线程,这些线程也会打印输出,有时输出不会显示出来,有时则会显示在运行Swank的控制台窗口中。我很想了解原因。
编辑:由于Daniel下面的答案,我现在知道其他线程没有将out绑定到REPL的输出上。此代码之所以有效,是因为您从运行它的位置传递了out。然而,我的新问题是,这段代码现在会阻塞每个线程,因此不是并行运行每个线程,而是一个接一个地运行每个线程,因此我需要一种更加线程感知的输出方法。
(defn sleeper-thread [out id t]
  "Sleep for time T ms"
  (binding [*out* out]
    (printf "%d sleeping for time %d\n" id t)
    (Thread/sleep t)
    (printf "%d slept\n" id)))

(defn test-threads [n out]
  (dotimes [x n]
    (.start (Thread. (#(sleeper-thread %1 %2 %3) out x (+ 2000 (rand-int 5000)))))))
2个回答

1
原因是,在其他线程中,*out*未绑定到REPL的流。尝试像这样做:
(let [repl-out *out*]
  (defn foo []
    (binding [*out* repl-out]
      ...)))

现在,当从另一个线程运行foo时,*out*将绑定到您定义函数时的内容(即SLIME REPL),因此打印将按预期工作。
或者,用于测试:
(defmacro future-output [& body]
  `(let [out# *out*]
     (future
       (binding [*out* out#]
         ~@body))))

注意:此代码未经测试,因为我当前没有可用的Clojure/SLIME环境,但是这段代码在几个月前曾经工作过。可能存在版本差异 新版本Clojure(1.3 Alpha 2)中:
  • 使用vars的代码路径现在对于常见情况下的 快,并且您必须明确要求动态绑定性。

谢谢Daniel。我不太明白你的第二个解决方案,我需要阅读一下关于future关键字的内容。如果代码在repl中定义,则您的第一个解决方案有效,但我通常使用slime-eval-buffer评估代码,它也会启动自己的线程,因此我将其更改为将out变量传递到线程中。 - justinhj
@justinhj:很高兴能帮到你!您还可以考虑在Clojure Google组上询问更多详细信息/建议。关于future:也许我在这里使用不当,因为它在另一个线程中评估其主体并缓存结果,因此在具有副作用的代码中使用它可能不是最好的选择,但那只是为了测试当我正在尝试*out*时... - danlei

0
如果你在使用cake时遇到了问题,那么你的项目根目录(即project.clj所在的位置)下应该有一个.cake/cake.log文件,其中记录了输出信息。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接