1

匿名SwingWorkerスレッドで非常に重いプロセスを実行しています。それまでの間、進行状況バーを使用して GUI に進行状況を報告しています。しかし、Swing スレッド化は私を悩ませています。それは単に時間内に何も更新していません。SwingWorkerスレッドから、および外部から GUI を更新しようとしたが、どちらも動作を拒否したため、その方法がわかりません。

負荷の高いワーカー スレッドの実行中に Swing UI を確実に更新するにはどうすればよいですか?

私が試したこと

これは機能しません (invokeLaterコマンドでのラップの有無にかかわらず)。

new LocalCompressor(compressor).execute();

while (!compressionDone) {
    SwingUtilities.invokeLater(new Runnable() {
    
        @Override
        public void run() {
            int percent = compressor.getPercentDone();
            progressBar.setValue(percent);
            statusLabel.setText(percent);
        }
    });
}

さらに、並行測定スレッドから UI を更新しようとしても機能しません。

class LocalCompressor extends SwingWorker<Void, Void> {

    // [...]
    
    public LocalCompressor(Compressor compressor) {
        this.compressor = compressor;
        
        // [...]
    }
    
    @Override
        protected Void doInBackground() {
        
            final Thread t1 = new Thread(new Runnable() {
            
                @Override 
                public void run(){
                    compressor.compress();
                }
            });
            
            final Thread t2 = new Thread(new Runnable() {
            
                @Override
                public void run() {
                
                    t1.start();
                    
                    while (t1.isAlive()) {
                        updateUI(compressor.getPercentDone());
                    }
                }
            
            });
            
            t2.start();
            
            return null;
        }
        
        // [...]
}
4

5 に答える 5

2

SwingWorkerを実際に使用しているわけではありません。ワーカーはすでにそれ自体のスレッドです。長時間実行されるコードをに入れる可能性がある場合は、doInBackground()そこに置いてください。次にpublish(Integer)、実際の進捗状況を呼び出して、process(List<Integer>)-methodで取得したチャンクを処理します。process()では、GUIを更新できます。これはEDT上にあります。

編集:実際、あなたが今していることは、いくつかのwhileループでポーリングしていることですが、これはちょっと電力を消費します。そのため、アルゴリズム内のイベントは、パーセントを取得するたびに、またはループが新しいラウンドを開始するたびに、またはそのようなものの方が良いと思います。

于 2012-10-03T19:39:11.713 に答える
1

ここですでに提供されている回答とアドバイスを拡張して、コーディングする方法の 1 つを次に示します。コンプレッサ自体にはコールバックを実行する機能がないと仮定していますが、完了した割合を尋ねることができます。

swingworker スレッド (doInBackground) 内で、実際の圧縮スレッドを開始します。次に、バックグラウンド スレッドでポーリング ループを開始し、UI を 1 秒間に数回更新します。UI スレッドに通知するには、publish を呼び出します。これにより、オーバーライドされたメソッド プロセスがイベント スレッドで定期的に呼び出されます。ここから、進行状況バーとステータス ラベルを安全に更新できます。

public class LocalCompressor extends SwingWorker<Void, Integer>
{
   private Compressor compressor;

   public LocalCompressor(Compressor compressor)
   {
      this.compressor = compressor;

      // [...]
   }

   @Override
   protected void done()
   {
      System.out.println("Compression is done.  Going to do something with it...");
   }

   @Override
   protected void process(List<Integer> chunks)
   {
      for (Integer percent : chunks)
      {
         progressBar.setValue(percent);
         statusLabel.setText(percent);      
      }
   }

   @Override
   protected Void doInBackground() throws Exception
   {
      final Thread t1 = new Thread(new Runnable()
      {
         @Override
         public void run()
         {
            compressor.compress();
         }
      });

      t1.start();

      while (t1.isAlive())
      {
         int percentDone = compressor.getPercentDone();
         publish(percentDone);
         Thread.sleep(200);
      }
      return null;
   }
}
于 2012-10-03T20:05:17.260 に答える
1

を使用する非常にシンプルで基本的な方法を試しましたSwingWorkerか? @Zhedar が以前に言ったように、SwingWorker はすでにそれ自体が Thread です。したがって、両方の内側のスレッド ( t1t2) を削除し、時間のかかるcompress()方法を で使用してくださいdoInBackground()

次のような非常に基本的なもの

class LocalCompressor extends SwingWorker<Void, Integer> {

    // .....
    // Your constructor here
    // .....

    @Override
    protected Void doInBackground() throws Exception {
        compress();
        return null;
    }

    @Override
    protected void process(List<Integer> chunks) {
        for (Integer chunk : chunks) {
            progressBar.setValue(chunk);
            statusLabel.setText(chunk);
        }
    }
}

ここで、このcompress()メソッドを の内部に移動SwingWorkerする必要があり、どこかに a が必要publish()ですpublish(getPercentDone())

private void compress() {

    // .....

    publish(getPercentDone());

    // .....

}

これは通常、 で行われる方法SwingWorkerです。

于 2012-10-03T21:21:52.283 に答える
0

あなたは生産者/消費者パターンを従業員にすることができます...

これが本当に基本的な概念です...

public class ProducerComsumer {

    public static void main(String[] args) {
        new ProducerComsumer();
    }

    public ProducerComsumer() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                JPanel panel = new JPanel(new GridBagLayout());
                panel.setBorder(new EmptyBorder(12, 12, 12, 12));

                JProgressBar progressBar = new JProgressBar();
                panel.add(progressBar);

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(panel);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                Producer producer = new Producer();
                producer.start();

                Consumer consumer = new Consumer(producer, progressBar);
                consumer.start();
            }
        });
    }

    public class Producer extends Thread {

        private volatile float progress;
        private volatile boolean done;

        public Producer() {
            setPriority(NORM_PRIORITY - 1);
            setDaemon(true);
        }

        public float getProgress() {
            return progress;
        }

        public boolean isDone() {
            return done;
        }

        @Override
        public void run() {
            done = false;
            for (int index = 0; index < Integer.MAX_VALUE; index++) {
                progress = (float) index / (float) Integer.MAX_VALUE;
            }
            done = true;
            System.out.println("All done...");
        }
    }

    public class Consumer extends Thread {

        private Producer producer;
        private JProgressBar progressBar;

        public Consumer(Producer producer, JProgressBar progressBar) {
            setDaemon(true);
            setPriority(NORM_PRIORITY - 1);
            this.producer = producer;
            this.progressBar = progressBar;
        }

        public JProgressBar getProgressBar() {
            return progressBar;
        }

        public Producer getProducer() {
            return producer;
        }

        @Override
        public void run() {
            while (!producer.isDone()) {
                updateProgress();
                try {
                    sleep(1000);
                } catch (InterruptedException ex) {
                    Logger.getLogger(ProducerComsumer.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            updateProgress();
        }

        protected void updateProgress() {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    int progress = Math.round(getProducer().getProgress() * 100f);
                    System.out.println("Update progress to " + progress);
                    getProgressBar().setValue(progress);
                }
            });
        }
    }
}

値を試しThread.setPriorityてみて、違いが生じるかどうかを確認してください

于 2012-10-03T20:45:25.733 に答える
0

LocalCompressor.execute()への呼び出しがブロックされていると仮定しています (それがどのようになるか知っています) 。その場合、while ループはすべて完了するまで実行されず、UI の更新の安定したストリームを取得するという目的が無効になります。

これ、またはそれに似たものを試してください:

    LocalCompressor comp = new LocalCompressor(compressor);

    SwingUtilities.invokeLater(new Runnable() {

        @Override
        public void run() {
            while (!compressionDone) {
                int percent = compressor.getPercentDone();
                progressBar.setValue(percent);
                statusLabel.setText(percent);
            }
        }
    });
    comp.execute();
}
于 2012-10-03T19:39:43.650 に答える