Scala學習整理[第三十章 Actor和併發]

第三十章 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?")
  }
}

//構造Actor類 ,定義收到消息的反應
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 = {
    //構建Actor中心繫統 ,Akka系統消耗比較大,一個應用最好只構建一個
    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)

//工作Actor
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
  }
}

//主機Actor
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 = {
    //執行計算任務 -> 分配任務給worker
    case Calculate =>
      for (i <- 0 until nrOfMessages) workerRouter ! Work(i * nrOfElements, nrOfElements)
    //執行彙總任務 -> 計算結果彙報給listener
    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) {
    //創建akka系統
    val system = ActorSystem("PiSystem")
    //創建觀察者(因爲要注入到主actor中)
    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中
    */
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章