публикация/процесс делает SwingWorker медленным

Я пытался использовать SwingWorker для обновления индикатора выполнения. Ничего нового для меня. Я был немного ленив, поэтому не переопределил метод процесса. Вот упрощенный пример:

protected Void doInBackground() throws Exception {
    for (int i = 0; i < 10000; i++) {
        progressBar.setValue(i+1);            
    }
    return null;
}

На моем ПК обновление индикатора прогресса до 100% занимает около 6 секунд. Сегодня пятница, поэтому я подумал, давайте сделаем это по-другому, как часто описывают. Давайте используем методы публикации и обработки для обновления индикатора выполнения:

@Override
protected Void doInBackground() throws Exception {
    for (int i = 0; i < maximum; i++) {            
        publish(i+1);
    }
    return null;
}

@Override
protected void process(List<Integer> chunks) {        
    progressBar.setValue(chunks.get(chunks.size()-1));
}

Но теперь с методом публикации для обновления индикатора выполнения до 100% требуется 21 (вместо 6) секунд.

Почему?

Вот весь код (кадр с кнопкой «Пуск» и индикатором выполнения):

public class Gui extends JFrame {

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

    public Gui() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final JProgressBar progressBar = new JProgressBar();
        progressBar.setStringPainted(true);

        JButton btnStart = new JButton("Start");
        btnStart.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                new MySwingWorker(progressBar).execute();
            }
        });

        getContentPane().setLayout(new BorderLayout(3, 3));
        getContentPane().add(btnStart, BorderLayout.CENTER);
        getContentPane().add(progressBar, BorderLayout.SOUTH);
        pack();
        setVisible(true);
        setLocationRelativeTo(null);
    }

    private class MySwingWorker extends SwingWorker<Void, Integer> {

        private final int MAXIMUM = 100000000;
        private final JProgressBar progressBar;

        public MySwingWorker(JProgressBar progressBar) {
            this.progressBar = progressBar;
            this.progressBar.setMaximum(MAXIMUM);
            this.progressBar.setValue(0);
        }

        @Override
        protected Void doInBackground() throws Exception {
            for (int i = 0; i < MAXIMUM; i++) {
                //----------------------------------
                // TOGGLE BETWEEN THESE LINES:
                //progressBar.setValue(i+1);
                publish(i + 1);
                //-----------------------------------
            }
            return null;
        }

        @Override
        protected void process(List<Integer> chunks) {
            progressBar.setValue(chunks.get(chunks.size() - 1));
        }
    }
}

Спасибо за предложения...


person a3po    schedule 07.12.2012    source источник
comment
не смотрел внимательно - но ваша первая версия неверна: вы не должны обращаться к компоненту свинга в doInBackground   -  person kleopatra    schedule 07.12.2012
comment
да, я знаю, НО он работает без проблем и на 70% быстрее! 6 секунд вместо 21. Вот и вопрос ко мне: Что может быть на самом деле?   -  person a3po    schedule 07.12.2012
comment
все злые штуки, которые могут случиться при нарушении EDT ;-) Трудно обнаружить, ложно (но обязательно вырвет, когда вы меньше всего этого хотите), непредсказуемо. Так что просто не делайте этого (и большая часть производственного кода должна иметь защиту от этого). Проблема здесь, похоже, в том, что вы заливаете систему без необходимости: в реальном сценарии вы бы не пытались обновлять индикатор выполнения с более высокой частотой, чем его разрешение экрана, не так ли :-)   -  person kleopatra    schedule 07.12.2012
comment
Я проверю это на своей продуктивной системе. Анализ 500 000 000 сообщений. Какая разница во времени. Нужно ли пользователю ждать еще несколько минут только из-за индикатора выполнения? Я это проверю..   -  person a3po    schedule 07.12.2012
comment
я хочу сказать, что вам не нужны эти миллионы уведомлений: индикатор выполнения, заполняющий экран, будет иметь максимум пару тысяч пикселей, поэтому в любом случае ничего не изменится, если уведомление будет слишком мелким. Рабочий не заменяет мозг :-) В doInBackground разбивайте его на разумные куски и уведомляйте только каждые 100 000 или около того.   -  person kleopatra    schedule 07.12.2012
comment
Разницы в производительности нет, если   -  person a3po    schedule 08.12.2012
comment
Нет разницы в производительности, если что-то (даже несколько мс) выполняется в цикле! Но: миллионы уведомлений не будут обновлять индикатор выполнения миллионы раз, см. setValue() JProgressBar;) разбейте его на разумные куски, вот в чем проблема. Я не хочу изобретать алгоритмы для таких вещей. Но он работает нормально: просто опубликуйте (x%) на индикаторе выполнения...   -  person a3po    schedule 08.12.2012
comment
@a3po: Вы, конечно, можете продолжать говорить, что это работает нормально; но дело в том, что EDT не следует изменять из-за пределов EDT (google edt java oracle). Вы можете пойти дальше и проигнорировать это; но при этом ваш код будет сломан. Я рекомендую книгу Java Concurrency in Practice, если вы хотите больше узнать об этом (и понять, почему ваш код не работает).   -  person sbrattla    schedule 25.12.2012


Ответы (1)


Вы не должны публиковать каждое изменение, но, возможно, каждое 10-е:

for (int i = 0; i < maximum; i++) {
    doTheWork();
    if(i % 10 == 0) {
        publish(i+1);
    }
}
publish(maximum);
person fjf2002    schedule 01.05.2013