第三十章 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中
*/
}