Scala演员:JRE 1.5和1.6不同的行为
我的模拟是使用演员和Scala 2.8-Snapshot。在Java JRE 1.5中运行良好 – 所有40个齿轮(演员)都可以同时工作。使用Java JRE 1.6只有3个齿轮同时工作。我使用和不使用GUI进行了测试:两者都具有相同的结果。
我的GUI模拟可以在github:http://github.com/pmeiclx/scala_gear_simulation上找到 也许你记得my first problem with actors.解决这些问题后,我做了一个GUI的模拟,我得到了这个新的“奇怪”的行为。 以下是无GUI的代码: package ch.clx.actorversions import actors.Actor import actors.Actor._ import collection.mutable.ListBuffer case class ReceivedSpeed(gear: Gear) case object StartSync case class SyncGear(controller: GearController,syncSpeed: Int) object ActorVersion { def main(args:Array[String]) = { println("[App] start with creating gears") val gearList = new ListBuffer[Gear]() for (i <- 0 until 100) { gearList += new Gear(i) } val gearController = new GearController(gearList) gearController.start() gearController ! StartSync } } /** * CONTROLLER */ class GearController(nGears: ListBuffer[Gear]) extends Actor { private var syncGears = new ListBuffer[Gear] private var syncSpeed = 0 def act = { while(true) { receive { case StartSync => { println("[Controller] Send commands for syncing to gears!") var speeds = new ListBuffer[Int] nGears.foreach(e => speeds += e.speed) //Calc avg //var avgSpeed = speeds.foldLeft(0)(_ + _) / speeds.length //var avgSpeed = speeds.foldLeft(0) { (x,y) => x + y } / speeds.length syncSpeed = (0/:speeds)(_ + _) / speeds.length //Average over all gear speeds //TODO syncSpeed auf Median ausrichten println("[Controller] calculated syncSpeed: "+syncSpeed) nGears.foreach{e => e.start() e ! SyncGear(this,syncSpeed) } println("[Controller] started all gears") } case ReceivedSpeed(gear: Gear) => { println("[Controller] Syncspeed received by a gear ("+gear.gearId+")") //println("[Controller] mailboxsize: "+self.mailboxSize) syncGears += gear if(syncGears.length == nGears.length) { println("[Controller] all gears are back in town!") System.exit(0) } } case _ => println("[Controller] No match :(") } } } } /** * GEAR */ class Gear(id: Int) extends Actor { private var mySpeed = scala.util.Random.nextInt(1000) private var myController: GearController = null def speed = mySpeed def gearId = id /* Constructor */ println("[Gear ("+id+")] created with speed: "+mySpeed) def act = { loop { react { case SyncGear(controller: GearController,syncSpeed: Int) => { //println("[Gear ("+id+")] activated,try to follow controller command (form mySpeed ("+mySpeed+") to syncspeed ("+syncSpeed+")") myController = controller adjustSpeedTo(syncSpeed) } } } } def adjustSpeedTo(targetSpeed: Int) = { if(targetSpeed > mySpeed) { mySpeed += 1 self ! SyncGear(myController,targetSpeed) }else if(targetSpeed < mySpeed) { mySpeed -= 1 self ! SyncGear(myController,targetSpeed) } else if(targetSpeed == mySpeed) { callController } } def callController = { println("[Gear ("+id+")] has syncSpeed") myController ! ReceivedSpeed(this) } } 解决方法
简短的答案:改变您的控制器使用循环/反应,而不是在/接收
actors库检测到它正在运行哪个Java版本,如果它是1.6(而不是IBM的VM),则它使用捆绑版本的JSR-166y fork连接线程池,因此在底层实现方面存在很大差异,具体取决于Java版本。 fork / join线程池使用一种两级队列进行任务。每个工作线程都有一个队列,并且有一个池的共享队列。源于fork / join线程的任务直接转到fork / join线程的队列而不是通过主队列。线程间任务窃取用于保持线程忙,有助于避免饥饿。 在您的情况下,启动齿轮的所有任务都将在队列中运行控制器的线程。因为您在该actor中使用while / receive,所以它永远不会放弃线程,所以它不会直接在其队列中执行任务。其他线程不断忙于3档,所以他们从不试图从运行控制器的线程中窃取工作。结果是其他装备演员永远不会执行。 切换到控制器中的循环/反应应该解决问题,因为在每个循环中,actor让线程调度一个新的任务,这个任务最终会在队列的后面,所以其他任务将被执行。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |