【问题标题】:Concrete example of a defaultUncaughtExceptionHandlerdefaultUncaughtExceptionHandler 的具体示例
【发布时间】:2014-04-20 01:06:25
【问题描述】:

我曾多次遇到这样的情况,期货中的异常很难追踪(并且已经在这里问了一个问题,为什么某些异常似乎永远不会发生,这个问题不是我的旧问题的欺骗)并决定尝试设置“默认未捕获的异常处理程序”。

但是我不能让它工作。我尝试使用 reify 并尝试使用代理。就好像什么事都没发生一样。

这是重现问题的最小案例:

REPL> (Thread/setDefaultUncaughtExceptionHandler
  (proxy [Thread$UncaughtExceptionHandler] []
    (uncaughtException [thread throwable]
      (do (println (-> throwable .getCause .getMessage))
          ; (error "whatever...") ; some 'timbre' logging if you have timbre
      ))))
nil
REPL> (future (do (Thread/sleep 100) (/ 0 0)))
#<core$future_call$reify__6267@c2909a1: :pending>
REPL>

我尝试了 println,我尝试使用音色记录到一个文件,我尝试向一个临时文件吐口水,我试图通过 deref'ing 来强制运行未来......显然从不调用默认的未捕获异常处理程序。

谁能给我看一个交互式/REPL 示例,说明一个工作的默认未捕获异常处理程序实际上捕获了一个异常?

作为一个额外的问题:一旦设置了默认的未捕获异常处理程序,是否有一种简单的方法可以“查看”它已设置?当您测试该功能并多次调用 setDefaultUncaughtExceptionHandler 时,REPL 中会发生什么?是否只考虑最后一个处理程序?

【问题讨论】:

    标签: exception clojure uncaught-exception uncaughtexceptionhandler


    【解决方案1】:

    期货并非如此运作。异常不会逃逸。

     (deref (future (do (Thread/sleep 100) (/ 0 0))))
    
     ExecutionException java.lang.ArithmeticException: Divide by zero
        java.util.concurrent.FutureTask.report (FutureTask.java:122)
        java.util.concurrent.FutureTask.get (FutureTask.java:192)
        clojure.core/deref-future (core.clj:2108)
        clojure.core/future-call/reify--6267 (core.clj:6308)
        clojure.core/deref (core.clj:2128)
    

    Java 的 FutureTask.run() 的核心是答案...

    boolean ran;
    try {
        result = c.call();
        ran = true;
    } catch (Throwable ex) {
        result = null;
        ran = false;
        setException(ex);
    }
    

    这为未来响应的最终消费者节省了问题。如果我们稍微重构一下测试用例,我们可以清楚地看到异常处理程序的工作:

    (Thread/setDefaultUncaughtExceptionHandler
        (proxy [Thread$UncaughtExceptionHandler] []
          (uncaughtException [thread throwable]
            (do (println "****" (-> throwable .getMessage))
            ))))
    

    我删除了 getCause 因为我们不一定有一个...

    (.start (Thread. #(/ 0 0)))
    

    标准输出的产量...

    **** Divide by zero
    

    如果您所追求的(正如您的评论所建议的)是一个具有适当异常处理程序的线程,您是否考虑过类似以下内容:

    (defn exception-safe-queue-reader [source sink]
        (try
            (let [message (source)]
                (sink message))
            (catch Throwable t
                (println "****" t))
        (recur source sink))
    

    这(对我而言)是比使用 uEH 更标准的 Java 习惯用法 - 即,如果您要处理错误,则直接在代码中处理它们。无论您是使用裸 Java 线程还是将来启动它都无关紧要。

    【讨论】:

    • thx,这有助于让事情变得更清晰,但是......我有一个系统,事情非常解耦,广泛使用队列和生产者/消费者。所以,在这种情况下,我正在开始一个未来,它是一个消费者从队列中读取内容。所以我永远不需要取消那个未来。在这种情况下使用未来是不正确的吗?我应该如何启动一个不间断运行的线程,消耗队列中的东西(并且在应用程序的生命周期中活着)?我应该回到 Java 领域还是提供一个 clojure 抽象,既能很好地工作又能很好地考虑异常?
    • 啊,我想未来就像糖一样好,只是火和忘记线程。请参阅我上面的后记,了解我将如何在不使用 uEH 的情况下在 Java 或 Clojure 中处理此类事情。如果您正在处理队列周围的大量控制流,请考虑查看 core.async。
    • +1,再次感谢您的帮助...我可能会按照您的建议进行操作。我仍然希望有一种“简单”的方法来报告任何“丢失”的异常:例如,为了能够确定我需要在代码中的哪个位置直接处理这些异常。顺便说一句,你真的是指最后一行的 (recur source sink) 吗?
    • 是的 - 事件循环的重要之处在于循环!和事件,我猜。抱歉,如果这有点做作,我今天正在努力拼凑 sn-ps。祝你好运。
    • 啊,不知道 Clojure 的 recur 构造:我没有意识到它正在递归到 exception-safe-queue-reader 所以我想知道函数的名称在哪里:)
    猜你喜欢
    • 1970-01-01
    • 2021-02-25
    • 2021-11-18
    • 2010-11-21
    • 1970-01-01
    • 2012-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多