2010-02-18 24 views
16

Mô phỏng của tôi đang sử dụng diễn viên và Scala 2.8-Ảnh chụp nhanh. Trong Java JRE 1.5 nó chạy tốt - tất cả 40 bánh răng (diễn viên) đang làm việc cùng một lúc. Sử dụng Java JRE 1.6 chỉ 3 bánh răng đang hoạt động đồng thời. Tôi đã thử nghiệm nó có và không có GUI: cả hai đều cho kết quả tương tự.Diễn viên Scala: Hành vi khác nhau trên JRE 1.5 và 1.6

mô phỏng của tôi với GUI có sẵn trên github: http://github.com/pmeiclx/scala_gear_simulation

Có lẽ bạn nhớ để my first problem with actors. Sau khi giải quyết những vấn đề này, tôi đã làm một GUI cho mô phỏng và tôi có hành vi "lạ" mới này.

Dưới đây là đoạn code mà không 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) 
    } 
} 

Trả lời

8

Câu trả lời ngắn: thay đổi điều khiển của bạn để sử dụng vòng lặp/phản ứng thay vì trong khi/nhận

Các diễn viên Phát hiện thư viện mà phiên bản Java nó đang chạy trên, và nếu nó là 1.6 (và không phải của IBM VM) nó sử dụng một kèm theo phiên bản của JSR-166y fork join thread pool, do đó, có một sự khác biệt đáng kể trong việc triển khai bên dưới tùy thuộc vào phiên bản Java.

Nhóm ngã ba/ghép nối sử dụng một loại hàng đợi hai cấp cho các tác vụ.Mỗi luồng công nhân có một hàng đợi và có một hàng đợi được chia sẻ cho nhóm. Các nhiệm vụ có nguồn gốc từ một ngã ba/tham gia chủ đề đi trực tiếp vào ngã ba/tham gia hàng đợi của chủ đề thay vì thông qua hàng đợi chính. Nhiệm vụ ăn cắp giữa các chủ đề được sử dụng để giữ cho các chủ đề bận rộn và giúp tránh đói.

Trong trường hợp của bạn, tất cả các nhiệm vụ để khởi động các bánh răng sẽ kết thúc trên hàng đợi cho chuỗi chạy bộ điều khiển. Bởi vì bạn đang sử dụng trong khi/nhận trong đó diễn viên nó không bao giờ cho phép đi của thread, do đó, nó không bao giờ thực hiện các nhiệm vụ trực tiếp trên hàng đợi của nó. Các chủ đề khác liên tục bận rộn với 3 bánh răng, do đó, họ không bao giờ cố gắng để ăn cắp công việc từ các chủ đề chạy bộ điều khiển. Kết quả là các diễn viên bánh răng khác không bao giờ được thực hiện.

Chuyển sang vòng lặp/phản ứng trong bộ điều khiển sẽ khắc phục sự cố bởi vì trên mỗi vòng lặp, tác nhân cho phép chuyển chuỗi và lên lịch nhiệm vụ mới, sẽ kết thúc ở phía sau hàng đợi để các tác vụ khác trên đó được thực thi.

+0

FYI: Tôi đã giải thích vấn đề này với Philipp Haller và anh ấy đã sửa nó trong thân cây. Vì vậy, khi 2.8 được phát hành, nó sẽ không có vấn đề. https://lampsvn.epfl.ch/trac/scala/changeset/20950/scala/trunk/src/actors –

+0

Xin lỗi, tôi hơi bận. Với Snapshot mới nó hoạt động. Không hoàn hảo nhưng nó hoạt động. Cảm ơn bạn! – meip

1

Sử dụng Java JRE 1.6 chỉ có 3 bánh răng được làm việc cùng một lúc.

Bạn có nghĩa là:

  • chỉ có ba bánh răng thực hiện bất kỳ tiến bộ hướng tới tốc độ mục tiêu. Khi ba bánh răng đạt đến tốc độ mục tiêu, không còn bánh răng nào tạo ra bất kỳ tiến bộ nào.
  • chỉ ba bánh răng có tiến bộ tại một thời điểm. Khi một trong ba bánh răng đạt đến tốc độ mục tiêu, một thiết bị khác bắt đầu tiến triển cho đến khi tất cả các bánh răng đã đạt đến tốc độ mục tiêu.

Tôi đoán điều thứ hai?

Sự khác biệt trong hành vi được quan sát có thể là sự khác biệt trong việc triển khai JVM - có những thay đổi giữa JRE 1.5 và JRE 1.6. Một số thay đổi này có thể được tắt, ví dụ: bằng cách đặt cờ như thế này:

-XX:ThreadPriorityPolicy=1 

... nhưng hành vi thứ hai là cách hoàn toàn hợp lệ để thực thi mã của bạn. Nó không phải là những gì bạn mong đợi bởi vì nó vi phạm một khái niệm "công bằng" mà bạn có nhưng công việc lên lịch không. Bạn có thể thêm một số loại diễn viên Đồng hồ để đảm bảo rằng thiết bị được ưu tiên nhất không nhận được nhiều hơn (nói) 10 "bọ ve" nhiều hơn nam diễn viên ít được ưu tiên nhất.

Sự khác biệt giữa JRE cùng khó có thể nghiên cứu mà không biết:

  • chính xác JRE cập nhật phiên bản bạn đang sử dụng.
  • hệ điều hành nào bạn chạy.
  • số lượng CPU và lõi bạn có.
  • xem mã đã được biên dịch lại cho JRE 1.6 chưa.

Chúc may mắn!

Các vấn đề liên quan