【问题标题】:Background SwingWorker thread not executing a particular piece of code后台 SwingWorker 线程未执行特定代码段
【发布时间】:2014-11-15 19:08:48
【问题描述】:

我找不到更好的方式来表达问题标题中的问题,但希望你能理解......

我有以下代码...

[...]
final JDialog waitingDialog = optionPane.createDialog(optionPane, "Processing in backgroung");

waitingDialog.setLocationRelativeTo(homeFrame);
waitingDialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);


SwingWorker<Void, Void> backgroundThread = new SwingWorker<Void, Void>() {

    @Override
    protected Void doInBackground() throws Exception {
        // Background processing is done here
        System.out.println("[DEBUG] -- Background processing has started");
        MyCustomClass.initParameters();
        System.out.println("DEBUG] -- initParameters() has finished. Starting main processing now...");
        waitingDialog.setVisible(true);
        MyCustomClass.run();
        return null;
    }

    // This is called when background thread above has completed
    @Override
    protected void done() {
        waitingDialog.dispose();
    };
};
backgroundThread.execute();

[and does other things after this...]

...它执行得很好,直到它到达MyCustomClass.run()。当它应该启动这种方法时,它根本不这样做。应用程序不会暂停、停止或崩溃......它只是在后台处理完成后继续等待继续,但由于某种未知原因而没有发生。

MyCustomClass.run() 是一个static 方法,位于MyCustomClass。我怀疑问题可能出在它的名字上——运行——所以我把它改成了一些随机的东西,比如“doIt()”,但问题仍然存在。 该方法的第一行是一个简单的System.out.println("I got here"),但甚至没有打印出来。 我在方法调用之前和之后添加了断点并尝试调试,但这也无济于事。一切似乎都很好,但我找不到问题所在。


编辑
我问:如何使对话框仅在我希望显示时才显示?换句话说,脚本是:

  1. SwingWorker 启动时显示“等待”对话框
  2. 当 initParameter() 发出需要用户输入的选项对话框时隐藏/停止/禁用/处置对话框(按下按钮);
  3. 在处理恢复时再次显示等待对话框
  4. 由于后台处理已完成,请丢弃对话框

【问题讨论】:

    标签: java multithreading swing debugging swingworker


    【解决方案1】:

    我立即看到您正在从 SwingWorker 的 doInBackground 中进行 Swing 调用,这完全违背了使用 SwingWorker 或后台线程的目的。此外,您正在拨打的电话会显示一个模式对话框:

    waitingDialog.setVisible(true); // **** here ****
    MyCustomClass.run();   // **** not sure what this does or if this makes Swing calls or not.
    

    将暂停其下方的所有代码流,直到对话框不再可见,因为这就是模态对话框的功能。这就是阻止您的 SwingWorker 的原因。您必须尝试分离您的代码,以便仅在 Swing 事件线程上进行 Swing 调用,并将工作线程仅用于后台工作。

    相反

    • 创建您的 SwingWorker
    • 执行您的 SwingWorker
    • 然后在您的模态对话框中调用setVisible(true)

    在伪代码中:

    create modal dialog
    create SwingWorker
    Attach any PropertyChangeListeners to the SwingWorker (if needed)
    Execute the SwingWorker
    Display the modal dialog
    

    因此,我将在上面的代码中进行的第一个快速更改是从 SwingWorker 的 doInBackground() 方法中删除 waitingDialog.setVisible(true);,然后调用 waitingDialog.setVisible(true); 方法之后 执行 SwingWorker,即在 backgroundThread.execute() 之后;尤其。我知道似乎在 使其可见之前对其调用 dispose 似乎违反直觉,但相信我,这样会更好,因为 dispose 调用将被运行所需的时间阻塞后台线程。

    【讨论】:

    • 原谅我的无知,@Hovercrafter Full of Eels,但我对此很陌生,我不太明白你的意思。这是我第一次使用 SwingWorker,事实上,当我在寻找一种在不阻塞应用程序图形界面的情况下运行我的东西的方法时,我从一个示例中提取了这个想法。你介意发布一个例子吗?
    • @NickFanelli:通过搜索我的名字和 SwingWorker 一词,您可以找到许多我已经创建的示例,例如 this link。如果您想要更具体的示例,请考虑创建并发布您自己的 mcve,我会对其进行调整。
    • @NickFanelli:查看this example 获取我创建的示例程序。
    • @NickFanelli:所以我要在上面的代码中进行的第一个快速更改是从 SwingWorker 中删除 waitingDialog.setVisible(true);,然后调用它AFTER 执行 SwingWorker,尤其是在 backgroundThread.execute(); 之后。我知道在它上面调用 dispose 似乎违反直觉,但相信我,这样会更好。
    • @NickFanelli:如果所有initParameters() 所做的只是调用一个JOptionPane,那么它应该在Swing 事件线程上调用并且在后台线程之外调用。 JOptionPanes 是 Swing 组件,需要在事件线程上处理。同样,如果仍然卡住,请考虑创建 small, compilable, runnable test program
    【解决方案2】:

    首先,我接受了@Hovercraft Full Of Eels 的回答,因为它确实对我有帮助。就像我在 cmets 中所说的“只需将 SwingWorker 中的 setVisible(true) 替换为在其定义后立即使程序按预期运行”,这要归功于他的解释。

    但是,我需要更多(在原始问题的“编辑”部分中将其描述为脚本)。我设法使用以下代码实现了所需的行为...

    final JOptionPane optionPane = new JOptionPane("<html>Please wait...<br>Initializing parameters</html>", JOptionPane.INFORMATION_MESSAGE, JOptionPane.DEFAULT_OPTION,
                        new ImageIcon(Home.class.getResource("/path/to/a/gif/loading.gif")), new Object[]{}, null);
    
                final JDialog waitingDialog = optionPane.createDialog(optionPane, "MyApp");
                waitingDialog.setLocationRelativeTo(homeFrame);
                waitingDialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
    
    
                SwingWorker<Void, Void> backgroundThread1 = new SwingWorker<Void, Void>() {
                    @Override
                    protected Void doInBackground() throws Exception {
                        System.out.println("[DEBUG] -- Background processing of initParameters() has started");
                        MyCustomClass.initParameters();
                        System.out.println("[DEBUG] -- initParameters() has finished. Moving on...");
                        return null;
                    }
    
                    @Override
                    protected void done() {
                        waitingDialog.setVisible(false);
                        optionPane.setMessage("<html>Please wait... <br>Now working on your thing.</html>");
                    };
                };
                backgroundThread1.execute();
                waitingDialog.setVisible(true);
    
                SwingWorker<Void, Void> backgroundThread2 = new SwingWorker<Void, Void>() {
                    @Override
                    protected Void doInBackground() throws Exception {
                        System.out.println("[DEBUG] -- Start now the main processing in background");
                        MyCustomClass.run();
                        return null;
                    }
    
                    @Override
                    protected void done() {
                        waitingDialog.dispose();
                    };
                };
                backgroundThread2.execute();
                waitingDialog.setVisible(true);
    

    基本上(而且很明显)我所做的只是创建一个新的SwingWorker 并让每个例程在其中一个中运行。当第一个完成时,它所做的只是隐藏(setVisible(false))“waitingDialog” - 所以主线程不会被阻塞 - 并更改JOptionPane的消息以供对话框使用在第二个SwingWorker 启动后再次可见。

    我不知道这是否是一种好方法,甚至是最好/最聪明的方法,但它确实有效。如果有更好的方法,我很乐意学习。

    只有一个问题我还没有解决。在MyCustomClass.run() 例程中的某个地方,以某种方式执行它的第二个SwingWorker 正在被杀死/终止。 我怎么知道它正在发生?好吧,由于其他调试原因,我的例程中有一个循环在更新某个值时打印某个值,然后它突然停止打印(不,循环尚未结束),“waitingDialog”消失了没有其他事情发生。该应用程序不会崩溃或任何事情。我可以像什么都没发生一样继续使用它。没有抛出一个异常。

    我不知道这是怎么发生的,或者为什么会这样。任何帮助将不胜感激。

    【讨论】:

      【解决方案3】:

      众所周知,Swingworker 会在后台线程中吞下运行时异常。您可以通过在 Swingworker 的 done() 方法中执行“get()”来检测它们。搜索“SimpleSwingWorker”,您会找到如何执行此操作的示例。

      【讨论】:

      • 谢谢你,@Dave。我查看了“SimpleSwingWorker”搜索返回的问题,这些问题的答案对我来说确实很有意义。我还没有向我的应用提出申请,但一旦我能做到,我会回到这里告诉你我是否可以解决这个问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-04
      相关资源
      最近更新 更多