Frage

Meine Simulation wird mit Schauspielern und Scala 2.8-Snapshot. In Java JRE 1.5 läuft es gut - alle 40 Gänge (Schauspieler) arbeiten gleichzeitig. Mit Java JRE 1.6 nur 3 Gänge arbeiten gleichzeitig. Getestet habe ich es mit und ohne GUI. Beide gleiches Ergebnis

Meine Simulation mit GUI ist auf GitHub: http://github.com/pmeiclx/scala_gear_simulation

Vielleicht erinnern Sie mein erstes Problem mit Schauspielern . Nach der Lösung dieser Probleme habe ich eine GUI für die Simulation und ich bekam dieses neue „seltsames“ Verhalten.

Hier ist der Code ohne 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)
  }
}
War es hilfreich?

Lösung

Kurze Antwort: ändern Sie Ihre Controller verwenden Schleife / reagieren, anstatt während / empfangen

Die Schauspieler Bibliothek erkennt, welche Java-Version es läuft, und wenn es 1.6 ist (und nicht von IBM VM) verwendet es eine gebündelte Version der JSR-166Y Gabel verbindet Thread-Pool, so gibt es einen wesentlichen Unterschied in dem zugrunde liegenden Implementierung abhängig von Java-Version.

Die Gabel / join Thread-Pool verwendet eine Art von Zwei-Ebenen-Warteschlange für Aufgaben. Jeder Worker-Thread hat eine Warteschlange, und es gibt eine gemeinsame Warteschlange für den Pool. Aufgaben mit Ursprung in einer Gabel / join Faden geht direkt auf die Gabel / join Thread Warteschlange anstatt durch die Haupt-Warteschlange. Aufgabe zwischen Threads zu stehlen verwendet Threads beschäftigt und helfen, zu vermeiden Hunger zu halten.

In Ihrem Fall alle Aufgaben die Zahnräder für den roten Faden der Controller am Ende der Warteschlange zu starten. Weil Sie verwenden, während / empfangen in diesem Schauspieler lässt es nie den Faden gehen, so dass es nie direkt auf seiner Warteschlange die Aufgaben ausführt. Die anderen Themen sind ständig damit beschäftigt mit den drei Gängen, so dass sie nie läuft die Steuerung der Arbeit aus dem Thread zu stehlen versuchen. Das Ergebnis ist die anderen Getriebe Akteure niemals ausgeführt.

Die Umstellung auf Schleife / reagieren in der Steuerung sollte das Problem beheben, da auf jeder Schleife der Schauspieler gehen des Fadens und Zeitpläne können eine neue Aufgabe, die auf der Rückseite der Warteschlange am Ende wird so die anderen Aufgaben wird es sein ausgeführt wird.

Andere Tipps

  

Mit Java JRE 1.6 nur 3 Gänge arbeiten gleichzeitig.

Sie meinen, dass:

  • nur drei Gänge machen Fortschritte in Richtung der Zielgeschwindigkeit. Wenn die drei Gänge die Zielgeschwindigkeit nicht mehr Gänge erreichen machen Fortschritte.
  • nur drei Gänge Fortschritte machen zu jeder Zeit. Wenn einer der drei Gänge die Zielgeschwindigkeit erreicht, beginnt ein weiterer Gang Fortschritte machen, bis alle Zahnräder die Zielgeschwindigkeit erreicht haben.

Ich würde die zweite erraten?

Der Unterschied in der beobachteten Verhalten ist wahrscheinlich auf eine Differenz in dem JVM-Implementierungen - gibt es Änderungen zwischen JRE 1.5 und JRE 1.6. Einige dieser Veränderungen können abgeschaltet werden, beispielsweise durch eine Flagge wie diese Einstellung:

-XX:ThreadPriorityPolicy=1

... aber das zweite Verhalten ist eine völlig gültige Art und Weise Ihren Code auszuführen. Es ist einfach nicht, was Sie erwartet, weil sie eine Vorstellung von „Fairness“ verletzt, dass Sie haben aber die Arbeit Scheduler nicht. Man könnte eine Art von Clock Schauspielern hinzufügen, um sicherzustellen, dass die am meisten bevorzugte Gang erhält nicht mehr als (etwa) 10 „Ticks“ mehr als die am wenigsten favorisierte Schauspieler.

Der Unterschied zwischen der JREs ist schwer zu Forschung, ohne zu wissen:

  • genau, welche JRE Update-Versionen Sie verwenden.
  • , welches Betriebssystem Sie ausführen.
  • , wie viele CPUs und Cores Sie haben.
  • , ob der Code für JRE 1.6 neu kompiliert wurde.

Viel Glück!

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top