16

私のシミュレーションでは、アクターとScala2.8-スナップショットを使用しています。Java JRE 1.5では、正常に動作します。40個のギア(アクター)すべてが同時に動作しています。Java JRE 1.6を使用すると、3つのギアのみが同時に動作します。GUIを使用してテストした場合と使用しない場合でテストしました。どちらも同じ結果になります。

GUIを使用したシミュレーションは、githubで入手できます:http://github.com/pmeiclx/scala_gear_simulation

多分あなたは俳優との私の最初の問題を覚えています。これらの問題を解決した後、シミュレーション用の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)
  }
}
4

2 に答える 2

8

簡単な答え:while/receiveの代わりにloop/reactを使用するようにコントローラーを変更します

アクターライブラリは、実行中のJavaバージョンを検出し、1.6(IBMのVMではない)の場合は、バンドルバージョンのJSR-166yフォーク参加スレッドプールを使用するため、基盤となる実装に大きな違いがあります。 Javaバージョン。

フォーク/結合スレッドプールは、タスクに一種の2レベルのキューを使用します。各ワーカースレッドにはキューがあり、プール用の共有キューがあります。フォーク/ジョインスレッドで発生したタスクは、メインキューを経由するのではなく、フォーク/ジョインスレッドのキューに直接移動します。スレッド間のタスクスティーリングは、スレッドをビジー状態に保ち、飢餓を回避するために使用されます。

あなたの場合、ギアを開始するためのすべてのタスクは、コントローラーを実行しているスレッドのキューに入れられます。そのアクターでwhile/receiveを使用しているため、スレッドを解放することはなく、キューで直接タスクを実行することはありません。他のスレッドは常に3つのギアでビジー状態であるため、コントローラーを実行しているスレッドから作業を盗もうとすることはありません。その結果、他のギアアクターは実行されません。

コントローラでループ/反応に切り替えると、問題が解決するはずです。ループごとに、アクターはスレッドを解放し、新しいタスクをスケジュールします。新しいタスクはキューの最後に配置され、他のタスクが実行されます。

于 2010-02-20T15:59:18.013 に答える
1

Java JRE 1.6を使用すると、3つのギアのみが同時に動作します。

それはどういう意味ですか:

  • 目標速度に向かって前進するのは3つのギアだけです。3つのギアが目標速度に達すると、それ以上ギアは進みません。
  • 一度に進歩するのは3つのギアだけです。3つのギアの1つが目標速度に達すると、すべてのギアが目標速度に達するまで、別のギアが進行を開始します。

私は2番目を推測しますか?

観察された動作の違いは、おそらくJVM実装の違いによるものです。JRE1.5とJRE1.6の間に変更があります。これらの変更の一部は、たとえば次のようなフラグを設定することでオフに切り替えることができます。

-XX:ThreadPriorityPolicy=1

...しかし、2番目の動作は、コードを実行するための完全に有効な方法です。それはあなたが持っている「公平さ」の概念に違反しているので、あなたが期待したものではありませんが、作業スケジューラはそうではありません。ある種の時計アクターを追加して、最も人気のあるギアが最も人気のないアクターよりも(たとえば)10個以下の「ティック」を受け取るようにすることができます。

JRE間の違いは、次のことを知らずに調査するのは困難です。

  • 使用しているJRE更新バージョンを正確に示します。
  • 実行しているOS。
  • 使用しているCPUとコアの数。
  • コードがJRE1.6用に再コンパイルされているかどうか。

幸運を!

于 2010-02-20T14:58:15.587 に答える