0

所以我实际上不确定问题到底是什么。它可能与我处理线程的方式有关,但我不知道如何解决这个问题。

该程序是一个具有不同“部落”生物的简单生物模拟,我希望拥有它,以便当我创建一个新部落时它在 GUI 中有自己的面板。现在这在我启动线程之前完美运行,但之后它总是冻结。我知道 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() (因为这是更好的方法),但这也不起作用。

编辑:我的问题可能是我试图从另一个不是 GUI2D 的对象(该方法所在的对象)调用 AddTribe 方法。也许如果我将所有代码粘贴到 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 线程调用的,但它仍然冻结 GUI。所以线程不是我的问题?

编辑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()
4

2 回答 2

3

关于 Swing 线程要记住的关键事项是:

  • 您不应该在特殊的 AWT/Swing 线程上进行长时间运行的计算,否则 GUI 将冻结(因为 Swing 线程永远没有机会处理 GUI 事件)。默认情况下,从 GUI 触发的任何活动(例如,由按钮上的事件处理程序等)都将在 Swing 线程上运行。如果需要很长时间,请将此类活动放入后台线程。
  • 您只能从 Swing 线程更新 GUI。这通常意味着后台线程应该用于SwingUtilities.invokeLater()将工作添加到 Swing 线程的队列中,或者使用SwingWorker辅助类。

另请参阅有关这些主题的 Java 教程:

更新:

您可以通过添加调试语句来检查哪个线程正在运行任何给定的代码,例如:

println(Thread.currentThread)

或使用调试器工具。它有助于使用适当的构造函数为您自己创建的任何线程提供可读的名称。

于 2015-05-05T12:13:14.283 回答
0

直接调用.run()aRunnable只会在当前线程上运行它。单独运行它的最简单方法是创建一个新的Threadstart()它:

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

每次按下按钮时都启动一个新线程,但效率有点低;例如,您可能更喜欢使用 aThreadPoolExecutorService来管理线程池。或者对于更高级/高性能的技术,您可以使用 akka Actors。

于 2015-05-07T16:22:12.360 回答