scala并发编程第8章习题1-3

五到七章先放一下,过段时间再看,akka还是比较给力的,最近忙着部署mesos和hive,过段时间也把部署和踩的一些小坑写一下,今天晚上换换脑子,做做akka的题

1

通过TimerActor类实现计时器,接收到含有超时设置变量t的Register消息后,该计时器应在过了变量t设置的时间后向发送Register消息的对象发回一条Timeout消息,该计时器必须能够接收多条Register消息

package com.linewell.chapter8

import akka.actor.{Props, ActorSystem, Actor}
import akka.actor.Actor.Receive

/** * Created by ctao on 15-12-13. */
object TimeOutTest extends App {


  class TimeActor extends Actor {
    override def receive: Receive = {
      case Register(x) => if(x<=10000) {
        Thread.sleep(x)
        sender ! TimeOut(x)
      } else{
        sender ! Stop
      }
      case _ =>
    }
  }

  class PrintTimerActor(receiverPath: String, outTime: Long) extends Actor {
    override def receive: Actor.Receive = {
      case Start => context.actorSelection(receiverPath) ! Register(outTime)
      case TimeOut(x) => println(s"receive sender $sender message $x ms")
        sender() ! Register(x + 1000)
      case Stop => println("shutdown")
        context.system.terminate()
    }
  }

  val system = ActorSystem("timeout")
  val timeActor = system.actorOf(Props[TimeActor], name = "time")
  val printTimerActor = system.actorOf(Props(new PrintTimerActor("/user/time", 1000)))
  printTimerActor ! Start
}


case class Register(t: Long)

case class TimeOut(time: Long)

case object Start

case object Stop

我的实现逻辑是定义一个Register样例类,一个超时样例类,其实这两个可以定义在一起,但为了区分职能这里还是分开定义,定义一个开始和一个结束的样例对象
printactor在接收到开始信号后就向指定路径的actor发送一个注册信号,timeoutactor在接收到注册信号后就会先休眠对应的时间,然后返回发送一个超时信息,printactor在收到超时信号后将打印超时信息,并再次向发送这个信号的actor发送一个注册信息,时间多了1s,当timeoutactor在接收大于10s的超时信号后则向发送者发送停止信号,printactor在接收到停止信号后将停止系统
scala并发编程第8章习题1-3_第1张图片

2

账户在收到send指令后发送到指定账户指定金额,一旦交易没完成的时候收到kill指令则终止交易,这个问题我是用偏函数做的,但感觉还是过于复杂,而且后面指令的连续发送问题也会出现少量bug

package com.linewell.chapter8

import akka.actor.{Actor, ActorSystem, Props}
import akka.event.Logging

/** * Created by ctao on 2015/12/15. */
object Account extends App {

  /** * 开始 */
  case object Start

  /** * 转账 * @param receiveActor 接收者 * @param sendMoney 金额 */
  case class Sender(receiveActor: String, sendMoney: Double)

  /** * 转账 * @param sendActor 发送者 * @param receiveMoney 金额 */
  case class Receiver(sendActor: String, receiveMoney: Double)

  /** * 结束 * @param i 多次握手 */
  case class End(i: Int)

  /** * 终止 */
  case class Kill(i:Int)

  /** * 账户actor * @param name 账户名 * @param money 金额 * @param otherAccount 交易账户 */
  class AccountActor(name: String, var money: Double, otherAccount: String) extends Actor {
    private val log = Logging(context.system, this)
    /** * 内部的一个交易表 */
    private val moneyBuffer = collection.mutable.ArrayBuffer[Double](0.0)

    /** * 开始交易,如果收到Start信号则开始交易,将此时的状态设置为交易中,利用偏函数 */
    def startTransAction: Receive = {
      case Start => log.info("start transAction")
        context.become(inTransAction)
    }

    /** * 进行交易 */
    def inTransAction: Receive = {
      /** * 发送指令 */
      case Sender(receiveActor, sendMoney) =>
        log.info(s"$name sender $sendMoney to $receiveActor")

        /** * 内部表维护一个减少的金额 */
        moneyBuffer += -sendMoney

        /** * 给交易账户发送一个接收指令 */
        context.actorSelection(otherAccount) ! Receiver(self.path.toString, sendMoney)

      /** * 接收指令 */
      case Receiver(senderActor, receiveMoney) =>
        log.info(s"$name receive $receiveMoney from $senderActor")

        /** * 内部表加入接收金额 */
        moneyBuffer += receiveMoney

      /** * 终止交易 */
      case Kill(0) => log.info("kill transaction")
        context.become(killTransAction)
        self ! Kill(1)


      /** * 结束指令,如果是在交易时收到结束指令,且状态为0,则将偏函数设置为结束交易,并向自己发送一个结束信号 */
      case End(0) => log.info(s"end transAction $name")
        context.become(endTransAction)
        self ! End(1)
    }

    /** * 必须在结束偏函数中收到指令为1的才能结束交易,而收到这个指令始终为自己发送的 */
    def endTransAction: Receive = {
      case End(1) =>
        money += moneyBuffer.sum
        log.info(s"name $name money $money")
        moneyBuffer.clear()
        context.actorSelection(otherAccount) ! End(0)
        context.become(startTransAction)
    }

    /** *在kill指令收到后将清空链表,这个指令始终为自己发送 */
    def killTransAction:Receive = {
      case Kill(1) =>
        moneyBuffer.clear()
        log.info(s"name $name money $money")
        context.actorSelection(otherAccount) ! Kill(0)
        context.become(startTransAction)
    }

    override def receive: Actor.Receive = startTransAction

    override def unhandled(msg: Any) = {
      log.info(s"cannot handle message $msg in this state.")
    }

  }


  val system = ActorSystem("account")
  val a = system.actorOf(Props(new AccountActor("a", 1000,"/user/b")), name = "a")
  val b = system.actorOf(Props(new AccountActor("b", 1000,"/user/a")), name = "b")

  b ! Start
  a ! Start
  for (i <- 1 to 10) {
    a ! Sender("/user/b", i)
  }
  b ! Kill(0)
  Thread sleep 1000
  a ! End(0)

  a ! Start
  b ! Start
  for (i <- 1 to 10) {
    a ! Sender("/user/b", i)
  }
  a ! Kill(0)
  Thread sleep 1000
  system.terminate()
}

3

实现SessionActor类,使用SessionActor对象控制其他对象的Actor对象的访问操作,
SessionActor实例在接收到含有正确密码的StartSession消息后,应该将所有消息转发给Actor对象r,直到他收到EndSession消息为止

package com.linewell.chapter8

import akka.actor.Actor.Receive
import akka.actor.{ActorSystem, Props, Actor, ActorRef}

/** * Created by ctao on 15-12-14. */
object SessionActorTest extends App {

  case class Message(msg: String)

  case class StartSession(pass: String)

  case object EndSession

  class SessionActor(password: String, r: ActorRef) extends Actor {
    private var check = false

    override def receive: Receive = {
      case StartSession(pass) => if (pass == password) {
        check = true
        println("Success start session")
      } else println("Error pass")
      case Message(msg) => if (check) r ! Message(msg) else println("Please check password before send message")
      case EndSession => check = false
        println("End Session")
      case _ => println("ingore")
    }


  }


  class PrintActor extends Actor {
    override def receive: Actor.Receive = {
      case Message(msg) => println(s"receive from $sender : message $msg")
      case _ =>
    }
  }

  object SessionActor {
    def apply(password: String, actorRef: ActorRef) = Props(new SessionActor(password, actorRef))
  }


  val system = ActorSystem("session")
  val printActor = system.actorOf(Props[PrintActor], name = "print")
  val sessionActor = system.actorOf(SessionActor("ctao", printActor))

  sessionActor ! StartSession("cc")
  sessionActor ! StartSession("ctao")

  for (i <- 1 to 10)  sessionActor ! Message(s"the num is $i")

  sessionActor ! EndSession

  sessionActor ! Message("check test")

  system.terminate()

}

思想,消息分为三种,一种是校验password的消息,StartSeesion,一种是结束session生命周期的EndSession,一种是真正的消息,转发给其他actor的,
actor有两种,一种是seesionActor,一种是printActor,sessionActor内部维护一个校验标志,如果接收到StartSession而且密码正确则完成校验,设置校验标志为真,否则提示重新校验,如果收到普通消息,且校验标志为真,则完成转发,否则则提示进行校验,如果收到结束session标志,则将校验标注设置为false
printActor则完成打印
scala并发编程第8章习题1-3_第2张图片

你可能感兴趣的:(编程,scala,akka)