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.