Concurrency 如何从clojure futures中的错误中获取stacktraces?

Concurrency 如何从clojure futures中的错误中获取stacktraces?,concurrency,clojure,Concurrency,Clojure,我有一些非常独立的任务,这些任务是我使用futures衍生出来的。这些任务通过core.async/chan将某些事件传回主应用程序,或者只与db对话 其中一些未来正在悄然失败。我的日志中没有堆栈跟踪,或者在std{out,err}上没有堆栈跟踪。我已经试着在futures中用 (try (do-stuff) (catch Exception e (log/error e)) 只是想在我的日志中获得一些输出,但这——令人惊讶--没用 我唯一的选择是旋转另一个线程,在循环中执行以下操

我有一些非常独立的任务,这些任务是我使用futures衍生出来的。这些任务通过core.async/chan将某些事件传回主应用程序,或者只与db对话

其中一些未来正在悄然失败。我的日志中没有堆栈跟踪,或者在std{out,err}上没有堆栈跟踪。我已经试着在futures中用

(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)))))

这是否表明我根本不应该使用期货?我是否应该使用代理,即使不需要向这些代理发送任何消息?

该行为与Java
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之前立即打印“catch ya”的。然后在deref上返回catch块将来返回的值


再一次,底线是-它的工作原理与Java futures几乎相同。

这个问题实际上是由Stuart Sierra解决的。去那里读它,因为它是值得的。简而言之,他的优雅解决方案是设置默认的未捕获异常处理程序:

;; Assuming require [clojure.tools.logging :as log]
(Thread/setDefaultUncaughtExceptionHandler
  (reify Thread$UncaughtExceptionHandler
    (uncaughtException [_ thread ex]
      (log/error ex "Uncaught exception on" (.getName thread)))))

我不熟悉core.async,因此我无法评论您在该库中使用futures的适当性。但是,如果期货因异常而失败,那么取消对期货的引用应该重新抛出异常。这对期货仍然有效吗?根据文章:“另一个折痕:未来中的异常总是被未来捕获。除非有东西调用future.get(Clojure中的deref),否则不会抛出异常。”