Scala.swing在更新时冻结

时间:2015-05-05 10:05:53

标签: multithreading scala scala-swing

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

该程序是一个简单的boid模拟,具有不同的“tribed”boid,我想拥有它,以便当我创建一个新的部落时,它在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()
  }

接下来是(runnable)线程的代码:

  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:尝试创建一个可以运行该代码的新runnable,但它仍然以某种方式从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 个答案:

答案 0 :(得分:3)

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

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

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

<强>更新

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

println(Thread.currentThread)

或使用调试工具。它有助于使用appropriate constructor为您自己创建的任何线程提供可读的名称。

答案 1 :(得分:0)

直接在telnet <yourservername> smtp HELO <yourservername and then enter> MAIL from: <mailaddr and then enter> RCPT to: <mailaddr and then enter> DATA 354 End data with <CR><LF>.<CR><LF> From: <mailaddr and then enter> To: <mailaddr and then enter> Subject: test This is test . 250 2.0.0 Ok: queued as 4C67244003 QUIT <and then enter> 221 2.0.0 Bye Connection closed by foreign host. 上调用.run()将在当前线程上运行它。单独运行它的最简单方法是创建一个新的RunnableThread

start()

每次按下按钮时启动一个新线程虽然效率不高;您可能更愿意例如使用case ButtonClicked(e) if (e == addButton) => new Thread(new AddTribeRunnable(...)).start() 来管理线程池。或者,对于更先进/更高性能的技术,您可以使用akka Actors。