第三十章 Actor和併發
package SecondWithProgrammingInScala
/**
* Actor和併發
*
* Java採用同步 synchronized ,鎖的機制能有效的控制共享數據的操作 ,但是也帶來一些死鎖和效率上的問題 ,非常依賴程序猿的經驗
* Scala提供了一套模型 :actor ,不共享數據 ,依賴消息傳遞 (scala原生的actor已替換成akka.actor)
*/
import java.util.concurrent.TimeUnit
import akka.actor.{Actor, ActorSystem, Props}
import scala.concurrent.duration.Duration
case class Greeting(who: String)
object Talk {
def unapply(question: String): Option[String] = question match {
case "hello" => Option("hi")
case "nice to meet you" => Option("nice to meet you ,too")
case _ => Option("what?")
}
}
class HelloActor extends Actor {
def receive = {
case Greeting(who) => println("Welcome ," + who)
case Talk(answer) => println(answer)
case _ => "enh?"
}
}
object ActorTest {
def main(args: Array[String]): Unit = {
val system = ActorSystem("HelloSystem")
val helloActor = system.actorOf(Props[HelloActor], name = "helloActor")
helloActor ! Greeting("liaoad")
helloActor ! "hello"
helloActor ! "how r you"
helloActor ! "nice to meet you"
}
}
/**
* akka 主從actor工作系統
*
* 使用多個工作器 計算pi的值
*/
import akka.actor._
import akka.routing.{RoundRobinGroup, RoundRobinPool}
sealed trait PiMessage
case object Calculate extends PiMessage
case class Work(start: Int, nrOfElements: Int) extends PiMessage
case class Result(value: Double) extends PiMessage
case class PiApproximation(pi: Double, duration: Duration)
class Worker extends Actor {
def receive = {
case Work(start, nrOfElements) =>
sender ! Result(calculatePiFor(start, nrOfElements))
}
def calculatePiFor(start: Int, nrOfElements: Int): Double = {
println("開始計算pi ,當前節點: start[%s]".format(start))
var acc = 0.0
for (i <- start until (start + nrOfElements))
acc += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1)
acc
}
}
class Master(nrOfWorkers: Int, nrOfMessages: Int, nrOfElements: Int, listener: ActorRef) extends Actor {
var pi: Double = _
var nrOfResults: Int = _
val start: Long = System.currentTimeMillis
val workerRouter = context.actorOf(
RoundRobinPool(nrOfWorkers).props(Props[Worker]), name = "workerRouter")
def receive = {
case Calculate =>
for (i <- 0 until nrOfMessages) workerRouter ! Work(i * nrOfElements, nrOfElements)
case Result(value) =>
println("節點計算結束 ,當前值: pi[%s] ,value[%s]".format(pi, value))
pi += value
nrOfResults += 1
if (nrOfResults == nrOfMessages) {
listener ! PiApproximation(pi, duration = Duration(System.currentTimeMillis - start, TimeUnit.MICROSECONDS))
context.stop(self)
}
}
}
class Listener extends Actor {
def receive = {
case PiApproximation(pi, duration) ⇒
println("\n\tPi approximation: \t\t%s\n\tCalculation time: \t%s"
.format(pi, duration))
context.system.terminate()
}
}
object Pi extends App {
calculate(nrOfWorkers = 4, nrOfElements = 10000, nrOfMessages = 10000)
def calculate(nrOfWorkers: Int, nrOfElements: Int, nrOfMessages: Int) {
val system = ActorSystem("PiSystem")
val listener = system.actorOf(Props[Listener], name = "listener")
val master = system.actorOf(Props(new Master(nrOfWorkers, nrOfMessages, nrOfElements, listener)), name = "master")
master ! Calculate
}
/**
* 由結果可見 ,通過路由池(類似線程池) ,主機將消息有序的分配給各個工作器
* 因爲actor結構 ,採用消息的分發和收集實現並行 ,消息的接受是有序的 ,達成了同步
* 各個Woker是交替開始和結束的 ,但只有彙總的時候Pi值纔有序的進行變化
*
* akka是一個比較有效的並行同步工具 ,現在也已經推廣到了Java中
*/
}