如何调试CompletableStage死锁? [英] How to debug CompletableStage deadlocks?

查看:137
本文介绍了如何调试CompletableStage死锁?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近遇到的最困难的调试问题是异步操作之间的死锁。例如,给定两个 CompletionStage 链,其中第一条链调用依赖于第二条链完成的方法,第二条链调用依赖于完成条的方法第一链的。在现实生活中这不是很明显,因为这种依赖关系往往被隐藏起来,有时僵局涉及三个以上的参与者。

The most difficult debugging problem I've run across recently is deadlocks between asynchronous operations. For example, given two CompletionStage chains, where the first chain invokes a method that depends upon the completion of the second chain, and the second chain invokes a method that depends upon the completion of the first chain. It isn't this obvious in real-life because the dependency tends to be hidden and sometimes deadlocks involve more than three parties.

部分问题是没有找出 CompletableStage 正在等待什么的方法。这是因为操作引用了 CompletableStage ,而不是相反。

Part of the problem is that there is no way to find out what a CompletableStage is waiting on. This is because an operation references a CompletableStage, not the other way around.

大多数调试器提供一定程度的死锁现在可以进行检测,但这仅适用于线程。 如何调试CompletableStage链之间的死锁?

Most debuggers provide some level of deadlock detection nowadays, but this only applies to threads. How does one debug deadlocks between CompletableStage chains?

推荐答案

我最终做了以下事情:


  • 在每个 CompletionStage 链的末尾,安排将获得的事件超时后触发:

  • At the end of each CompletionStage chain, schedule an event that will get fired after a timeout:

Set<Object> knownDeadlocks = ConcurrentHashMap.newKeySet();
// ...
Future<?> deadlockListener = scope.getScheduler().schedule(() ->
{
    if (knownDeadlocks.add(Throwables.getStackTraceAsString(context)))
        log.warn("Possible deadlock", context);
}, DEADLOCK_DURATION.toMillis(), TimeUnit.MILLISECONDS);


  • 使用 CompletionStage.handle()如果阶段按预期完成,则禁用deadlockListener:

  • Use CompletionStage.handle() to disable deadlockListener if the stage completes as expected:

    return stage.handle((value, throwable) ->
    {
        // WARNING: By design, CompletionStage.whenComplete() suppresses any exceptions thrown by its argument, so we use handle() instead.
        deadlockListener.cancel(false);
        if (throwable == null)
            return value;
        return rethrowException(throwable);
    });
    


  • 为了完整性,您还可以:

  • For completeness, you also have:

    /**
     * Rethrows a {@code Throwable}, wrapping it in {@code CompletionException} if it isn't already wrapped.
     *
     * @param <T>       the return type expected by the caller
     * @param throwable a Throwable
     * @return an undefined value (the method always throws an exception)
     * @throws CompletionException wraps {@code throwable}
     */
    public <T> T rethrowException(Throwable throwable)
    {
        if (throwable instanceof CompletionException)
            throw (CompletionException) throwable;
        if (throwable == null)
            throwable = new NullPointerException("throwable may not be null");
        // According to https://stackoverflow.com/a/49261367/14731 some methods do not wrap exceptions
        throw new CompletionException(throwable);
    }
    


  • 这篇关于如何调试CompletableStage死锁?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆