难以让scala actor在并行范围内工作

时间:2012-11-09 19:02:00

标签: list scala parallel-processing actor

计算机网络软件开发类的家庭作业,教授让我们为端口1-1024构建一个端口扫描器,以便对本地主机运行。练习的目的是演示使用actor的任务级并行性。 prof提供了按顺序扫描每个端口的代码。我们要创建一个并行执行此操作的版本,每个处理器的actor或系统可用的超线程。目标是花时间完成所有端口1-1024的完整扫描,并将并行扫描的结果与串行扫描的结果进行比较。这是我的并行扫描代码:

import java.net.Socket
import scala.actors._
import Actor._
import scala.collection.mutable.ArrayBuffer

object LowPortScanner {

  var lastPort = 0
  var openPorts = ArrayBuffer[Int]()
  var longestRunTime = 00.00
  var results = List[Tuple3[Int, Range, Double]]()

  val host = "localhost"
  val numProcs = 1 to Runtime.getRuntime().availableProcessors()
  val portsPerProc = 1024 / numProcs.size
  val caller = self

  def main(args: Array[String]): Unit = {

    //spawn an actor for each processor that scans a given port range
    numProcs.foreach { proc =>
      actor {
        val portRange: Range = (lastPort + 1) to (lastPort + portsPerProc)
        lastPort = lastPort + portsPerProc
        caller ! scan(proc, portRange)
      }
    }

    //catch results from the processor actors above
    def act {
      loop {
        reactWithin(100) {
          //update the list of results returned from scan
          case scanResult: Tuple3[Int, Range, Double] =>
            results = results ::: List(scanResult)

          //check if all results have been returned for each actor
          case TIMEOUT =>
            if (results.size == numProcs.size) wrapUp

          case _ =>
            println("got back something weird from one of the port scan actors!")
            wrapUp
        }
      }
    }

    //Attempt to open a socket on each port in the given range
    //returns a Tuple3[procID: Int, ports: Range, time: Double
    def scan(proc: Int, ports: Range) {
      val startTime = System.nanoTime()
      ports.foreach { n =>
        try {
          println("Processor " + proc + "is checking port " + n)

          val socket = new Socket(host, n)

          //println("Found open port: " + n)
          openPorts += n

          socket.close
        } catch {
          case e: Exception =>
          //println("While scanning port " + n + " caught Exception: " + e)
        }
      }
      (proc, ports, startTime - System.nanoTime())
    }

    //output results and kill the main actor
    def wrapUp {
      println("These are the open ports in the range 1-1024:")
      openPorts.foreach { port => println(port) }
      results.foreach { result => if (result._3 > longestRunTime) { longestRunTime = result._3} }
      println("Time to scan ports 1 through 1024 is: %3.3f".format(longestRunTime / 1000))
      caller ! exit
    }
  }
}

我有一个四核i7,所以我的numProcs = 8.在这个硬件平台上,每个proc actor应扫描128个端口(1024/8 = 128)。我的意图是proc1 actor扫描0 - 128,proc2应扫描129-256等...但是,这不是正在发生的事情。一些演员最终与其他演员在同一范围内工作。下面的输出示例说明了问题:

处理器2检查端口1
处理器7检查端口385
处理器1正在检查端口1
处理器5检查端口1
处理器4检查端口1
处理器8检查端口129
处理器3检查端口1
处理器6检查端口257
处理器1正在检查端口2 处理器5正在检查端口2 处理器1正在检查端口3
处理器3正在检查端口2 处理器5检查端口3
处理器1正在检查端口4

修改

最终“工作”代码:

import java.net.Socket
import scala.actors._
import Actor._
import scala.collection.mutable.ArrayBuffer

object LowPortScanner {

  var lastPort = 0
  var openPorts = ArrayBuffer[Int]()
  var longestRunTime = 00.00
  var results = List[Tuple3[Int, Range, Double]]()

  val host = "localhost"
  val numProcs = 1 to Runtime.getRuntime().availableProcessors()
  val portsPerProc = 1024 / numProcs.size
  val caller = self
  val procPortRanges = numProcs.foldLeft(List[Tuple2[Int, Range]]()) { (portRanges, proc) =>
    val tuple2 = (proc.toInt, (lastPort + 1) to (lastPort + portsPerProc))
    lastPort += portsPerProc
    tuple2 :: portRanges
  }

  def main(args: Array[String]): Unit = {

    //spawn an actor for each processor that scans a given port range
    procPortRanges.foreach { proc =>
      actor {
        caller ! scan(proc._1, proc._2)
      }
    }

//catch results from the processor actors above
def act {
  loop {
    reactWithin(100) {
      //update the list of results returned from scan
      case scanResult: Tuple3[Int, Range, Double] =>
        results = results ::: List(scanResult)

      //check if results have been returned for each actor
      case TIMEOUT =>
        if (results.size == numProcs.size) wrapUp

      case _ =>
        println("got back something weird from one of the port scan actors!")
        wrapUp
    }
  }
}

    //Attempt to open a socket on each port in the given range
    //returns a Tuple3[procID: Int, ports: Range, time: Double
    def scan(proc: Int, ports: Range) {
      val startTime = System.nanoTime()
      ports.foreach { n =>
        try {
          println("Processor " + proc + "is checking port " + n)

          val socket = new Socket(host, n)

          //println("Found open port: " + n)
          openPorts += n

          socket.close
        } catch {
          case e: Exception =>
          //println("While scanning port " + n + " caught Exception: " + e)
        }
      }
      (proc, ports, startTime - System.nanoTime())
    }

    //output results and kill the main actor
    def wrapUp {
      println("These are the open ports in the range 1-1024:")
      openPorts.foreach { port => println(port) }
      results.foreach { result => if (result._3 > longestRunTime) { longestRunTime = result._3} }
      println("Time to scan ports 1 through 1024 is: %3.3f".format(longestRunTime / 1000))
      caller ! exit
    }
  }
}

1 个答案:

答案 0 :(得分:3)

  

在此硬件平台上,每个proc actor应扫描128个端口(1024/8 = 128)。

除了你有

val portsPerProc = numProcs.size / 1024

和8/1024是0.请注意,您还有一个一个一个错误,导致每个actor扫描比portsPerProc多一个端口,它应扫描lastPort to (lastPort + portsPerProc) - 1或{{ 1}}。

对于未来,如果你有一个不同的问题,你应该单独问一下:)但是在这里你有一个非常明显的竞争条件:所有演员都试图执行

(lastPort + 1) to (lastPort + portsPerProc)

兼任。想想当(例如)演员1和2在任何演员到达第二行之前执行第一行时会发生什么。