簡體   English   中英

如何使從線程引發的未經檢查的異常傳播到主線程?

[英]How can I make an unchecked exception thrown from a thread to be propagated to main thread?

我找不到一種方法(既不能通過SO也不能調試我的代碼)來允許將從胎面引發的異常傳播到主線程。 我已經嘗試使用Thread.setUncaughtExceptionHandler()CompletableFuture.exceptionally() .handle() ,...

關鍵是,使用這些機制(在我調試的時候),實際的處理是在工作線程上執行的,而不是在主線程上執行的-我無法設法將其到達主線程。

總的來說,我正在編寫一個測試,並且如果在工作線程中引發異常,則它永遠不會到達運行測試的主線程,即使出現問題也可以使測試通過。

我需要該異常會引發異步; 我不能等待將來完成,因為我需要立即從主界面立即返回一個Stream(PipedStream)而不發生阻塞。

我得到的唯一提示是控制台錯誤(並且僅當我使用傳統的Thread + uncaughtExceptionHandler方法時,當我嘗試使用CompletableFuture時根本沒有日志):

Exception: com.example.exception.MyException thrown from the UncaughtExceptionHandler in thread "Thread-5"

如果我沒有定義異常處理程序,則為:

Exception in thread "Thread-5" com.example.MyException: Exception message

我提供一些代碼:

try {
    @SuppressWarnings("squid:S2095") final PipedOutputStream pos = new PipedOutputStream();
    final PipedInputStream pis = new PipedInputStream(pos);

    CompletableFuture.runAsync(pipeDecryptorRunnable(inputStream, pos));

    return pis;

} catch (IOException e) {
    throw new CryptographyException(e.getMessage(), e);
}

pipeDecryptorRunnable內部是一個解密數據的CipherStream。 那里拋出異常。 但是我無法在主線程中將其捕獲並變得不可見。 從此方法返回的pis流用於讀取,並且工作線程在讀取prom pis實時解密數據。

編輯: UncaughtExceptionHandler作為類似問題的答案表明不適用於我的情況,因為處理程序代碼是由工作線程而不是主線程調用的。

感謝@RalfKleberhoff的技巧,我提供了我所尋找的解決方案。 事實是,為了實現所需的行為,需要線程間通信機制。 考慮到我已經在使用PipedStream ,我可以利用它來實現目標–我還認為,在某種解決方案中,涉及事件總線來從一個線程向另一個線程(主線程/工作線程)發出信號或進行通信,我認為某些事件總線庫也可以實現它。

回到管道流中,我需要在主線程中讀取pis並在工作線程中寫入其連接的pos 因此,當在worker中引發異常時,我需要主線程來注意到這一點。

為此,您可以擴展PipedOutputStream類,添加一個方法以在發生異常時向連接的管道發出信號。 同樣,您需要擴展連接的PipedInputStream以在發生異常時發出信號,存儲異常並覆蓋read方法,以檢查是否首先發生異常,在這種情況下,將異常包裝在讀取的IOException中方法。

這里的代碼:

/**
 * This piped stream class allows to signal Exception between threads, allowing an exception produced in the writing
 * thread to reach the reading thread.
 *
 * @author Gerard on 10/11/2017.
 */
public class ExceptionAwarePipedOutputStream extends PipedOutputStream {

    private final ExceptionAwarePipedInputStream sink;

    public ExceptionAwarePipedOutputStream(ExceptionAwarePipedInputStream sink) throws IOException {
        super(sink);
        this.sink = sink;
    }

    /**
     * Signals connected {@link ExceptionAwarePipedInputStream} that an exception ocurred allowing to propagate it
     * across respective threads. This works as inter thread communication mechanism. So it allows to the reading thread
     * notice that an exception was thrown in the writing thread.
     *
     * @param exception The exception to propagate.
     */
    public void signalException(Throwable exception) {
        sink.signalException(exception);
    }
}

·

/**
 * This piped stream class allows to signal Exception between threads, allowing an exception produced in the writing
 * thread to reach the reading thread.
 *
 * @author Gerard on 10/11/2017.
 */
public class ExceptionAwarePipedInputStream extends PipedInputStream {

    private volatile Throwable exception;

    void signalException(Throwable exception) {
        this.exception = exception;
    }

    @Override
    public int read(byte[] b) throws IOException {
        final int read = super.read(b);
        checkException();
        return read;
    }

    @Override
    public synchronized int read() throws IOException {
        final int read = super.read();
        checkException();
        return read;
    }

    @Override
    public synchronized int read(byte[] b, int off, int len) throws IOException {
        final int read = super.read(b, off, len);
        checkException();
        return read;
    }

    private void checkException() throws IOException {
        if (exception != null) {
            throw new IOException(exception.getMessage(), exception);
        }
    }
}

客戶端代碼:

public InputStream decrypt(InputStream inputStream) {

    assert supportedStreamModes.contains(mode) : "Unsupported cipher mode for stream decryption " + mode;

    @SuppressWarnings("squid:S2095") final ExceptionAwarePipedInputStream pis = new ExceptionAwarePipedInputStream();
    final ExceptionAwarePipedOutputStream pos = newConnectedPipedOutputStream(pis);
    final Cipher decryptor = newDecryptorInitialized(inputStream);

    CompletableFuture.runAsync(
        pipeDecryptorRunnable(inputStream, pos, decryptor));

    return pis;
}

private ExceptionAwarePipedOutputStream newConnectedPipedOutputStream(ExceptionAwarePipedInputStream pis) {
    try {
        return new ExceptionAwarePipedOutputStream(pis);
    } catch (IOException e) {
        throw new CryptographyException(e.getMessage(), e);
    }
}

和異常處理部分( 注意線程信令):

private Runnable pipeDecryptorRunnable(InputStream inputStream, ExceptionAwarePipedOutputStream pos, Cipher decryptor) {
    return () -> {
        try {

            // do stuff... and write to pos

        } catch (Exception e) {
            // Signaling any (checked or unchecked) exception
            pos.signalException(new CryptographyException(e.getMessage(), e));
        } finally {
            closePipedStream(pos);
        }
    };
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM