Scala.swing зависает при обновлении

Так что я на самом деле не уверен, в чем проблема на самом деле. Вероятно, это относится к тому, как я обращаюсь со своими темами, но я не знаю, как это исправить.

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

Вот код для добавления панелей:

  val tribeBoxPanels = Buffer.empty[TribeBoxPanel]
  val tribeFrames = Buffer.empty[TribeSettingFrame]
  val addTribeFrame = ChooseTribeFrame.frame
  val addFlockingFrame = AddFlockingFrame.frame

  def addTribe(tribe: Tribe) = {
    pause()
    tribeFrames += new TribeSettingFrame(tribe)
    tribeBoxPanels += new TribeBoxPanel(tribe)
    refcontents
  }

  private def refcontents = {
    top.optionPanel.contents.clear()
    top.optionPanel.contents += new BoxPanel(Orientation.Vertical) {
      tribeBoxPanels.foreach(contents += _.tribeBoxPanel)
    }
    top.optionPanel.contents += new BoxPanel(Orientation.Horizontal) {
      contents += top.addTribeButton
    }
    top.optionPanel.contents += new BoxPanel(Orientation.Horizontal) {
      contents += top.vectorDebugButton
    }
    pause()
  }

И вот код для (запускаемого) потока:

  private var running = true

  def pause() = {
    if (running) {
      running = false
      t.stop()
    }
    else {
      running = true
      t = new Thread(BoidSimulation)
      t.start()
    }
  }

  var t = new Thread(BoidSimulation)
  t.start()

Я пытаюсь остановить поток при добавлении племени, но это не похоже на работу, GUI по-прежнему зависает. Я также попытался t.interrupt() (потому что это лучший способ), но это тоже не сработало.

РЕДАКТИРОВАТЬ: Может быть, моя проблема в том, что я пытаюсь вызвать метод AddTribe из другого объекта, который не является GUI2D (объект, в котором находится метод). Может быть, если я вставлю весь свой код в класс GUI2D, он будет работать?

РЕДАКТИРОВАТЬ 2: Попытка вызова метода, как это:

def invoke(tribe: Tribe) = new Runnable() { def run() = addTribe(tribe)  }

Не помогло, хотя бар, в который я пытаюсь добавить материал, все еще зависает.

Я также попытался распечатать поток, который вызывает метод, и вот что я получил:

Thread[Thread-2,5,main]                  <- println(t)

Thread[AWT-EventQueue-0,6,main]          <- This call works

Thread[AWT-EventQueue-0,6,main]          <- This call works

Thread[AWT-EventQueue-0,6,main]          <- This call works

Thread[AWT-EventQueue-0,6,main]          <- After this it freezes.

Таким образом, метод вызывается из потока AWT, но он все еще замораживает графический интерфейс. Таким образом, поток не моя проблема?

Редактировать 3: я думаю, что нашел свою проблему! Это довольно задом наперед. Поскольку метод вызывается одним нажатием кнопки, Swing пытался завершить код, предназначенный для потока вычислений, что и привело к его зависанию. Теперь мне нужно выяснить точную противоположность SwingUtilities.invokelater()

Редактировать 4: Попытка создания нового исполняемого файла, который будет запускать этот код, но он все равно вызывается из потока AWT. В чем причина этого и как заставить код работать из потока вычислений?

class AddTribe(dist: Int, maxSpeed: Int, boidMass: Int, color: Color, behavior: Behavior, boidAmount: Int) extends Runnable {
    println(Thread.currentThread())
    def run() = BoidSimulation.addTribe(dist: Int, maxSpeed: Int, boidMass: Int, color: Color, behavior: Behavior, boidAmount: Int)
  }

case ButtonClicked(e) if (e == addButton) => {
        new AddTribe(distSlider.value, maxSpdSlider.value / 10, massSlider.value / 10, color, new Flocking(separationSlider.value, alignmentSlider.value, cohesionSlider.value), boidSlider.value).run()

2 ответа

Ключевые моменты, которые следует помнить о потоках Swing:

  • Вы не должны выполнять длительные вычисления в специальном потоке AWT/Swing, иначе графический интерфейс зависнет (потому что поток Swing никогда не получает возможности обрабатывать события GUI). Любая активность, запускаемая из графического интерфейса (например, обработчиком события для кнопки и т. Д.), Будет по умолчанию выполняться в потоке Swing. Поместите такие действия в фоновые темы, если они будут занимать много времени.
  • Вы должны обновлять только графический интерфейс из потока Swing. Обычно это означает, что фоновые потоки должны использовать SwingUtilities.invokeLater() добавить работу в очередь для потока Swing или использовать SwingWorker вспомогательный класс.

Смотрите также учебники по Java по этим темам:

Обновить:

Вы можете проверить, какой поток выполняет любой данный бит кода, добавив операторы отладки, такие как:

println(Thread.currentThread)

или используя инструменты отладчика. Это помогает дать читаемые имена любым потокам, которые вы создаете сами, используя соответствующий конструктор.

Призвание .run() на Runnable непосредственно запустит его в текущем потоке. Самый простой способ запустить его отдельно - это создать новый Thread а также start() Это:

case ButtonClicked(e) if (e == addButton) =>
    new Thread(new AddTribeRunnable(...)).start()

Запуск нового потока для каждого нажатия кнопки несколько неэффективен; вы можете предпочесть, например, использовать ThreadPoolExecutorService управлять пулом потоков. Или для более продвинутой / высокопроизводительной техники, которую вы можете посмотреть, используя akka Actors.

Другие вопросы по тегам