SwingWorker 上的异常无法捕获

发布于 2024-12-26 07:08:52 字数 4608 浏览 0 评论 0原文

我一直在使用 Java 线程,以便为在管道中运行进程提供 GUI 平台。我已经成功解决了 SwingWorker 的许多问题,但这个问题似乎难以理解。

我的 SwingWorker 看起来像:

SwingWorker<Boolean,Object> worker = new SwingWorker<Boolean,Object>() {
         @Override 
        public Boolean doInBackground() {
             return launchBlockingPipelineProcess(process, instance, project, logger, state);
         }

         @Override
         protected void done(){
             boolean success = false;
             try{
                 success = get();
                 if (!success){
                     state.setTaskFailed(true);
                     }
                 if (process.getStatus().equals(Status.Interrupted)){
                     state.setTaskInterrupted(true);
                     }
             }catch (Exception ex){
                state.setTaskFailed(true); 
                }

             processCompleted(process, success, state);
         }

    };

我用它来运行 Java 进程;启动代码的简化版本是:

try{
    Class<?> target_class = Class.forName(main_class);

    CommandInstance instance = (CommandInstance)target_class.newInstance();
    CommandFunctions.ProcessState state = instance.execute(args);

}catch (InvocationTargetException e){
    throw new PipelineException("Java process '" + this.getName() + "." + uid + "' encountered exception: " + e.getCause().getMessage());
}catch (Exception e){
    throw new PipelineException("JavaProcess encountered exception invoking target: " + e.getMessage());
    }

进程本身在代码周围有一个 try-catch 块,用于从文件加载一些数据。然而,尽管被包裹在两层 try-catch 块中(实际上是三层,如果算上 done() 方法),当加载器抛出异常时,它会打印堆栈跟踪,但异常不是捕获,并且 SwingWorker 线程挂起(代码暂停,因此我无法再强制中断)。

这种异常挂起也发生在其他情况下;最令人困惑的是,在其他看似相同的情况下,异常被捕获并且线程正常退出。

尽管我会继续搜索,但我在网上找不到太多相关信息。我不是 Swing 线程方面的专家,所以我希望有人能够对此类问题有深入的了解。我希望这对我来说是一个非常愚蠢的错误:)

编辑:@Adrian,这是堆栈跟踪。它似乎停了下来......非常奇怪:

java.io.EOFException
    at java.io.RandomAccessFile.readFully(RandomAccessFile.java:399)
    at mgui.io.standard.nifti.Nifti1Dataset.readVolBlob(Nifti1Dataset.java:2179)
    at mgui.io.standard.nifti.Nifti1Dataset.readDoubleVol(Nifti1Dataset.java:1916)
    at mgui.io.standard.nifti.NiftiVolumeLoader.setGrid3DBlocking(NiftiVolumeLoader.java:186)
    at mgui.io.domestic.shapes.VolumeFileLoader.setGrid3D(VolumeFileLoader.java:237)
    at mgui.io.domestic.shapes.VolumeFileLoader.getGrid3D(VolumeFileLoader.java:139)
    at mgui.io.domestic.shapes.VolumeFileLoader.getGrid3D(VolumeFileLoader.java:97)
    at minc.MincFunctions.create_volume_atlas_masks(MincFunctions.java:5240)
    at minc.MincFunctions.run_command(MincFunctions.java:153)
    at mgui.command.CommandInstance.execute(CommandInstance.java:87)
    at mgui.pipelines.JavaProcess.run(JavaProcess.java:141)
    at mgui.pipelines.PipelineFunctions.launchBlockingPipelineProcess(PipelineFunctions.java:238)
    at mgui.pipelines.PipelineFunctions.launchPipelineProcess(PipelineFunctions.ja

EDIT2:在 Eclipse 中调试,我可以在抛出它的行上的断点处停止(好吧,之前一步);此时的堆栈跟踪是:

Nifti1Dataset.readVolBlob(short) line: 2179 
Nifti1Dataset.readDoubleVol(short) line: 1916   
NiftiVolumeLoader.setGrid3DBlocking(Grid3D, int, ProgressUpdater) line: 186 
NiftiVolumeLoader(VolumeFileLoader).setGrid3D(Grid3D, int, ProgressUpdater) line: 237   
NiftiVolumeLoader(VolumeFileLoader).getGrid3D(VolumeInputOptions, int, ProgressUpdater) line: 139   
NiftiVolumeLoader(VolumeFileLoader).getGrid3D(int) line: 97 
MincFunctions.create_volume_atlas_masks() line: 5278    
MincFunctions.run_command(String) line: 153 
MincFunctions(CommandInstance).execute(String[]) line: 87   
JavaProcess.run(String[], long) line: 141   
PipelineFunctions.launchBlockingPipelineProcess(PipelineProcessInstance, String, InterfaceProject, String, PipelineState) line: 238 
PipelineFunctions.launchPipelineProcess(PipelineProcessInstance, String, InterfaceProject, String, boolean, PipelineState) line: 78 
PipelineFunctions.launchPipelineProcess(PipelineProcessInstance, boolean, PipelineState) line: 52   
PipelineProcessInstance.launch(boolean) line: 187   
InterfacePipeline.launch(boolean) line: 388 
PipelineLauncher.doInBackground() line: 57  
PipelineLauncher.doInBackground() line: 1   
SwingWorker$1.call() line: 277  
FutureTask$Sync.innerRun() line: 303    
SwingWorker$2(FutureTask<V>).run() line: 138    
PipelineLauncher(SwingWorker<T,V>).run() line: 316  
ThreadPoolExecutor$Worker.runTask(Runnable) line: 886   
ThreadPoolExecutor$Worker.run() line: 908   
Thread.run() line: 662  

I have been working with Java threads in order to provide a GUI platform for running processes in a pipeline. I've managed to work through a number of issues with SwingWorker, but this one is seemingly incomprehensible.

My SwingWorker looks like:

SwingWorker<Boolean,Object> worker = new SwingWorker<Boolean,Object>() {
         @Override 
        public Boolean doInBackground() {
             return launchBlockingPipelineProcess(process, instance, project, logger, state);
         }

         @Override
         protected void done(){
             boolean success = false;
             try{
                 success = get();
                 if (!success){
                     state.setTaskFailed(true);
                     }
                 if (process.getStatus().equals(Status.Interrupted)){
                     state.setTaskInterrupted(true);
                     }
             }catch (Exception ex){
                state.setTaskFailed(true); 
                }

             processCompleted(process, success, state);
         }

    };

I use this to run a Java process; a simplified version of the launching code is:

try{
    Class<?> target_class = Class.forName(main_class);

    CommandInstance instance = (CommandInstance)target_class.newInstance();
    CommandFunctions.ProcessState state = instance.execute(args);

}catch (InvocationTargetException e){
    throw new PipelineException("Java process '" + this.getName() + "." + uid + "' encountered exception: " + e.getCause().getMessage());
}catch (Exception e){
    throw new PipelineException("JavaProcess encountered exception invoking target: " + e.getMessage());
    }

The process itself has a try-catch block around code which loads some data from a file. However, despite being wrapped in two layers of try-catch blocks (actually three, if you count the done() method), when the loader throws an Exception it prints a stack trace, the Exception is not caught, and the SwingWorker thread hangs (the code halts so I can no longer force an interrupt).

This sort of Exception hanging has occurred in other circumstances as well; most puzzlingly, in other -- seemingly identical -- cases the Exception is caught and the thread exits gracefully.

I haven't been able to find much about this online, although I'll continue to search. I am no expert in Swing threading, so I was hoping someone might have an insight about this sort of issue. I'd love it to be a very stupid error on my part :)

EDIT: @Adrian, here is the stack trace. It seems to stall half way... very strange:

java.io.EOFException
    at java.io.RandomAccessFile.readFully(RandomAccessFile.java:399)
    at mgui.io.standard.nifti.Nifti1Dataset.readVolBlob(Nifti1Dataset.java:2179)
    at mgui.io.standard.nifti.Nifti1Dataset.readDoubleVol(Nifti1Dataset.java:1916)
    at mgui.io.standard.nifti.NiftiVolumeLoader.setGrid3DBlocking(NiftiVolumeLoader.java:186)
    at mgui.io.domestic.shapes.VolumeFileLoader.setGrid3D(VolumeFileLoader.java:237)
    at mgui.io.domestic.shapes.VolumeFileLoader.getGrid3D(VolumeFileLoader.java:139)
    at mgui.io.domestic.shapes.VolumeFileLoader.getGrid3D(VolumeFileLoader.java:97)
    at minc.MincFunctions.create_volume_atlas_masks(MincFunctions.java:5240)
    at minc.MincFunctions.run_command(MincFunctions.java:153)
    at mgui.command.CommandInstance.execute(CommandInstance.java:87)
    at mgui.pipelines.JavaProcess.run(JavaProcess.java:141)
    at mgui.pipelines.PipelineFunctions.launchBlockingPipelineProcess(PipelineFunctions.java:238)
    at mgui.pipelines.PipelineFunctions.launchPipelineProcess(PipelineFunctions.ja

EDIT2: Debugging in Eclipse, I can halt at a breakpoint on the line where it is thrown (well, one step before); the stack trace at that point is:

Nifti1Dataset.readVolBlob(short) line: 2179 
Nifti1Dataset.readDoubleVol(short) line: 1916   
NiftiVolumeLoader.setGrid3DBlocking(Grid3D, int, ProgressUpdater) line: 186 
NiftiVolumeLoader(VolumeFileLoader).setGrid3D(Grid3D, int, ProgressUpdater) line: 237   
NiftiVolumeLoader(VolumeFileLoader).getGrid3D(VolumeInputOptions, int, ProgressUpdater) line: 139   
NiftiVolumeLoader(VolumeFileLoader).getGrid3D(int) line: 97 
MincFunctions.create_volume_atlas_masks() line: 5278    
MincFunctions.run_command(String) line: 153 
MincFunctions(CommandInstance).execute(String[]) line: 87   
JavaProcess.run(String[], long) line: 141   
PipelineFunctions.launchBlockingPipelineProcess(PipelineProcessInstance, String, InterfaceProject, String, PipelineState) line: 238 
PipelineFunctions.launchPipelineProcess(PipelineProcessInstance, String, InterfaceProject, String, boolean, PipelineState) line: 78 
PipelineFunctions.launchPipelineProcess(PipelineProcessInstance, boolean, PipelineState) line: 52   
PipelineProcessInstance.launch(boolean) line: 187   
InterfacePipeline.launch(boolean) line: 388 
PipelineLauncher.doInBackground() line: 57  
PipelineLauncher.doInBackground() line: 1   
SwingWorker$1.call() line: 277  
FutureTask$Sync.innerRun() line: 303    
SwingWorker$2(FutureTask<V>).run() line: 138    
PipelineLauncher(SwingWorker<T,V>).run() line: 316  
ThreadPoolExecutor$Worker.runTask(Runnable) line: 886   
ThreadPoolExecutor$Worker.run() line: 908   
Thread.run() line: 662  

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

简单气质女生网名 2025-01-02 07:08:53

是的,可以从 SwingWorker 的方法 done() 中获取 Exception(s),但需要严格命名每个线程,更多信息请参见 线程,尤其是@trashgod 的回答,我还没有找到另一种可能性

yes is possible take Exception(s) from SwingWorker's methods done(), but required strictly naming every thread, more in this thread, especially answer by @trashgod, and I haven't found another possibility of how to it another way

夏の忆 2025-01-02 07:08:53

我通过在 AWT 线程绘制 UI 时不进行干扰来解决类似的表问题。
我使用 invokeLater() 将我的线程与 UI 线程同步。

有关更多详细信息,请参阅 EventQueue

I solved a similar table problem by not interfering when the AWT thread was painting the UI.
I used invokeLater() to synchronize my thread with the UI thread.

See EventQueue for more details.

是你 2025-01-02 07:08:53

@Adrian,感谢您的建议;我不认为我直接从 SwingWorker 线程进行任何 UI 更新;我确实对 JTree 节点进行更新以指示流程的成功或失败,但我为此使用发布/流程机制,这应确保从 EDT 调用所有 UI 更新:

这些是从执行管道调用的侦听器处理程序:

@Override
public void pipelineTaskTerminated(DynamicPipelineEvent event, PipelineTask task) {
    publish(task);
}

@Override
public void pipelineTaskUpdated(DynamicPipelineEvent event, PipelineTask task) {
    // Here we can publish the update to a task status
    publish(task);

}

这是处理方法:

@Override
protected void process(List<PipelineTask> tasks) {

    // Update task listeners on the Event-Dispatch Thread
    for (int i = 0; i < tasks.size(); i++){
        PipelineTask task = tasks.get(i);
        InterfacePipeline pipeline = task.getPipeline();
        if (pipeline != null){
            task.fireStatusChanged();
            }
        }

}

最后,这是 JTree 处理事件的方式:

public void taskStatusChanged(PipelineTaskEvent e){
    if (model == null) return;
    model.nodeStructureChanged(this);
    tree.repaint();
}

除了(可能提供信息)之外,对 JTree< 的调用/code> 也不会导致更新UI,尽管它是从 EDT 调用的。强制树更新的唯一方法是单击节点本身。可能是新线程的问题,但可能是相关的。

我将通过从线程中消除任何潜在的 UI 更新来检查代码并进行实验,看看是否可以通过这种方式防止问题......敬请关注:)

编辑:即使完全删除这些更新,我也会抛出相同的异常。 ..

@Adrian, thanks for the suggestion; I do not think I am making any UI updates directly from the SwingWorker thread; I do make updates to JTree nodes to indicate the success or failure of a process, but I use the publish/process mechanism for this, which should ensure that all UI updates are called from the EDT:

These are listener handlers called from the executing pipeline:

@Override
public void pipelineTaskTerminated(DynamicPipelineEvent event, PipelineTask task) {
    publish(task);
}

@Override
public void pipelineTaskUpdated(DynamicPipelineEvent event, PipelineTask task) {
    // Here we can publish the update to a task status
    publish(task);

}

And here is the process method:

@Override
protected void process(List<PipelineTask> tasks) {

    // Update task listeners on the Event-Dispatch Thread
    for (int i = 0; i < tasks.size(); i++){
        PipelineTask task = tasks.get(i);
        InterfacePipeline pipeline = task.getPipeline();
        if (pipeline != null){
            task.fireStatusChanged();
            }
        }

}

Finally, here is how the JTree handles the event:

public void taskStatusChanged(PipelineTaskEvent e){
    if (model == null) return;
    model.nodeStructureChanged(this);
    tree.repaint();
}

As a (potentially informative) aside, this call to the JTree also does not result in an update of the UI, despite its being called from the EDT. The only way to force the tree to update is by clicking on the node itself. Possibly an issue for a new thread, but it may be related.

I'll go through the code and experiment by eliminating ANY potential UI updates from the thread, and see if the problem can be prevented that way... stay tuned :)

EDIT: Even removing these updates altogether I get the same Exception thrown...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文