我有一些非常独立的任务,我已经使用期货分拆了。这些任务通过core.async / chan将某些事件传递回主应用程序,或者只是与数据库通信。
其中一些未来现在无声无息。我的日志中没有堆栈跟踪,或者在std {out,err}上没有堆栈跟踪。我尝试用期货用
来控制fns中的代码(try (do-stuff)
(catch Exception e
(log/error e))
只是为了得到一些输出到我的日志中,但是 - 令人惊讶的是! - 没有用。
我唯一的选择是在循环中启动另一个执行以下操作的线程吗?
(let [m (Thread/getAllStackTraces)]
(doseq [e (.entrySet m)]
(log/error (.toString (.getKey e)))
(doseq [s (.getValue e)]
(log/error " " (.toString s)))))
这是一种表明我根本不应该使用期货的症状吗?我是否应该使用座席,即使没有必要向这些座席发送任何消息?
答案 0 :(得分:5)
该行为与Java Future
非常相似。在未来的块中,异常可能会被抛出并被捕获,并且行为与您期望的一样。如果未捕获到异常,则Future
无法在调用线程上重新抛出异常。当你真正得到它的价值时,它只会以ExecutionException
的形式这样做。这对应于Clojure中的deref。
让我们创建一个抛出某些东西的函数:
(defn die [] (throw (RuntimeException.)))
如果我将来将它包装起来,它可以正常工作:
user=> (def x (future (die)))
#'user/x
; Note: No exception here
user=> @x
RuntimeException user/die (NO_SOURCE_FILE:1)
; Bam! Exception thrown on deref, think of it as
; ExecutionException when getting failed future's value in Java
所以你可以在deref上捕获这个异常:
user=> (def x (future (die)))
#'user/x
(try @x (catch Exception e (println "Caught ya")))
Caught ya
nil
或者你可以在未来发现它:
user=> (def x
#_=> (future
#_=> (try (die)
#_=> (catch Exception e
#_=> (print "Caught ya!")
#_=> "Something"))))
#'user/x
Caught ya
user=> @x
"Something"
请注意在这种情况下如何打印"抓住你"在deref之前,当后台线程发生错误时立即发生。然后在deref上返回catch块将来返回的值。
再一次,底线是 - 它的工作原理与Java期货非常相似。
答案 1 :(得分:0)
Stuart Sierra here实际上解决了这个问题。去那里阅读它,因为它是值得的。简而言之,他优雅的解决方案是设置默认的未捕获异常处理程序:
;; Assuming require [clojure.tools.logging :as log]
(Thread/setDefaultUncaughtExceptionHandler
(reify Thread$UncaughtExceptionHandler
(uncaughtException [_ thread ex]
(log/error ex "Uncaught exception on" (.getName thread)))))