Как бороться с потоком SwingWorker, который идет медленно? - PullRequest
0 голосов
/ 30 марта 2019

Вряд ли я понимаю многопоточность. И, начав изучать его, я столкнулся с одной проблемой, которая пришла мне в голову. Недавно я написал одно простое приложение, и как только я получу новые знания о Java, я хочу улучшить свое приложение с помощью того, что узнал. Это похоже на простой графический интерфейс, который обновляет изображения каждый период времени. Я реализовал ActionListener и переопределил actionPerformed метод. Таймер с задержкой 15 мс, перекрасил JPanel класс и все заработало нормально. Но я думал, что обновление моего GUI с использованием таймера непосредственно в actionPerformed (я предполагаю, что это другой поток, но я почти не уверен) - плохая идея. Поэтому я решил изменить код и использовать SwingWorker. Я вызвал все свои методы для анимации внутри process () .. и снова приложение работало нормально, но оно стало очень медленным. Теперь я думаю, что не так? Почему он действует медленнее, чем раньше? Моя задержка таймера на самом деле не ждет 15 мс, она намного медленнее, хотя задержка такая же. Я ошибся с многопоточностью? Помоги мне понять это. Заранее спасибо

public class GameEngine() extends SwingWorker<Void, Drawable>
GamePanel gp; // ref to JPanel class
{
 public GameEngine(GamePanel gp)
 {
  this.gp = gp;
 }
}
protected void doInBackground()
{
 publish();
}
protected void process(List<Drawable> chunks)
{
 Timer timer = new Timer(15, e ->
 {
  //methods for animation 
  fall();
  animate();
  checkTouch();
 });
}

Какой-то код, который я оставил за пределами. Если вам это нужно, я могу написать ...

EDITION

Просто для ясности своей проблемы я приведу еще несколько примеров и дополнительное объяснение.

** Раньше было: **

public class GamePanel extends JPanel
{
 public void GamePanel()
 {
 GameEngine engine = new GameEngine(this);
 }
 //some images , variables etc...
  protected void paintComponent(Graphics g)
  super.paintComponent(g)
  g.drawImage(image1, x, y, null);
  g.drawImage(image2, w, z,null);
 ...
}
public class GameEngine () implements ActionListener
{
 GamePanel gp;
 Timer timer;
 public void GameEngine(GamePanel gp)
 {
  this.gp = gp;
  timer = new Timer( 15 , this );
 }
 public void actionPerformed()
 {
  //these methods repaint my GamePanel every 15ms.
  fall(); // make object (image) increment on Y Axis 
  animate(); // make another object (image) decrement on X Axis
  checkTouch(); // check if objects collided
 }
}

** Стал: **

public class GamePanel extends JPanel
{
 public void GamePanel()
 {
 GameEngine engine = new GameEngine(this);
 }
 //some images , variables etc...
  protected void paintComponent(Graphics g)
  super.paintComponent(g)
  g.drawImage(image1, x, y, null);
  g.drawImage(image2, w, z,null);
 ...
}
public class GameEngine () extends SwingWorker<Void, Drawable>
{
 GamePanel gp;
 Timer timer;
 public void GameEngine(GamePanel gp)
 {
  this.gp = gp;
 }
 protected void doInBackground()
 {
  process();
 }
 protected void progress()
 {
  timer = new Timer (15, e-> 
  {
   new ActionListener(new actionPerformed)
   {
      //these methods repaint my GamePanel every 15ms.
  fall(); // make object (image) increment on Y Axis 
  animate(); // make another object (image) decrement on X Axis
  checkTouch(); // check if objects collided
   }
  });
 }
 protected void done()
 {
 };
}

Когда я впервые его создал, я реализовал ActionListener и обновил свою панель с помощью таймера, объявленного в конструкторе. Я предположил, что он не защищен от потоков. Вот почему я передаю все в процессе метод, где я объявил таймер, который ActionListener в качестве лямбда-аргумента. Другими словами, я вызываю все методы для анимации в другом потоке. Наконец это стало медленнее, по сравнению с первым примером .. Я не понимаю

  1. Таймер из первого примера EDT или другой поток?
  2. Мой первый пример - потокобезопасен?
  3. Почему мой второй пример идет намного медленнее, чем первый?

Я слышал о НЕ обновлении вашего GUI за пределами EDT, это так?

1 Ответ

2 голосов
/ 30 марта 2019

В вашем вопросе недостаточно информации, чтобы ответить на него, но я отвечу на ваши вопросы о многопоточности и подразумеваемом вопросе о Swing и рендеринге и посмотрю, смогу ли я вам помочь.

Я думаю, что наиболее вероятной причиной замедления является ненужное обновление экрана. После того как пиксель был нарисован на холсте или в любом другом приложении, обычно вашему приложению не нужно перерисовывать его, если только оно не должно меняться; либо спрайт перемещается, либо какое-либо другое изображение в вашем приложении временно скрывает часть чертежа и затем нуждается в восстановлении.

Обычно перекрашивание новичка игнорирует это и просто перекрашивает всю окрашенную поверхность. Хотя это будет работать, это медленно; если вы делаете это несколько раз в цикле, то цикл будет казаться медленным.

Лучший способ сделать это - использовать прямоугольник, переданный в подпрограмму перерисовки, и перекрасить только его пересечение с перерисовываемой всей поверхностью вашей подпрограммой - это сокращает часть, которая должна быть перерисована, и, следовательно, время перерисовки.

Что касается многопоточности, я думаю, что полезно думать об этом так, как мы привыкли думать о вещах в однопроцессорном мире: компьютер какое-то время делает что-то, а затем останавливается в месте, которое вы не можете предсказать, и делает что-то в другой ветке на некоторое время и т. д. Вы не можете принять порядок, в котором все будет сделано, или сколько времени он потратит на каждую вещь и т. д.

С современными многоядерными компьютерами, возможно, что эти вещи фактически выполняются в одно и то же время, но я не знаю, что попытка представить это поможет вам.

...