停止演员后取消未来

时间:2018-04-08 14:05:34

标签: scala akka future actor

我有一个Master-Worker架构的应用程序。工人需要做大量长时间的工作。当我们需要时,师父需要杀死这份工作。

我已尝试不使用Future,工作人员在工作时无法收到任何讯息。所以我尝试使用Future代替。但是,当工人停止工作时,工作仍在运行。如何在停止演员后释放资源?

这是代码。

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

import scala.concurrent.Future
import scala.concurrent.duration._


object Main extends App {

  object StopTask

  case class DoTask(task: String)

  override def main(args: Array[String]): Unit = {
    val system = ActorSystem("ClusterSystem")
    val master = system.actorOf(Props[Master], "master")
    master ! "FooTask"
    import system.dispatcher
    system.scheduler.scheduleOnce(5 second) {
      master ! StopTask
    }
  }

  class Master extends Actor {
    val worker: ActorRef = context.actorOf(Props[Worker], "worker")

    def receive: Receive = {
      case task: String => worker ! DoTask(task)
      case StopTask => context stop worker
    }
  }

  class Worker extends Actor {

    import context.dispatcher

    override def postStop(): Unit = {
      println("Stopping task...")
    }

    def receive: Receive = {
      case DoTask(task) =>
        Future {
          // High loading job here
          while (true) {
            println(s"Doing $task...")
            Thread.sleep(1000)
          }
        }
    }
  }

}

输出是......

[INFO ] 2018-04-08 21:48:33,947 akka.event.slf4j.Slf4jLogger - Slf4jLogger started
[INFO ] 2018-04-08 21:48:34,244 akka.remote.Remoting - Starting remoting
[INFO ] 2018-04-08 21:48:34,463 akka.remote.Remoting - Remoting started; listening on addresses :[akka.tcp://ClusterSystem@127.0.0.1:49770]
[INFO ] 2018-04-08 21:48:34,466 akka.remote.Remoting - Remoting now listens on addresses: [akka.tcp://ClusterSystem@127.0.0.1:49770]
[INFO ] 2018-04-08 21:48:34,521 akka.cluster.Cluster(akka://ClusterSystem) - Cluster Node [akka.tcp://ClusterSystem@127.0.0.1:49770] - Starting up...
[INFO ] 2018-04-08 21:48:34,717 akka.cluster.Cluster(akka://ClusterSystem) - Cluster Node [akka.tcp://ClusterSystem@127.0.0.1:49770] - Registered cluster JMX MBean [akka:type=Cluster,port=49770]
[INFO ] 2018-04-08 21:48:34,718 akka.cluster.Cluster(akka://ClusterSystem) - Cluster Node [akka.tcp://ClusterSystem@127.0.0.1:49770] - Started up successfully
Doing FooTask...
[INFO ] 2018-04-08 21:48:34,777 akka.cluster.Cluster(akka://ClusterSystem) - Cluster Node [akka.tcp://ClusterSystem@127.0.0.1:49770] - Metrics collection has started successfully
[INFO ] 2018-04-08 21:48:35,017 akka.cluster.Cluster(akka://ClusterSystem) - Cluster Node [akka.tcp://ClusterSystem@127.0.0.1:49770] - Welcome from [akka.tcp://ClusterSystem@127.0.0.1:2560]
Doing FooTask...
Doing FooTask...
Doing FooTask...
Doing FooTask...
Stopping task...
Doing FooTask...
Doing FooTask...

我找到the way来杀死Future。但我不知道如何融入这个架构。希望有人帮助我。

1 个答案:

答案 0 :(得分:0)

由于我最初的答案(通过递归链接它来破坏未来的执行)并没有真正解决问题,我做了一些研究,并从Roland Kuhn博士那里看到了这个想法:{{ 3}}

  

...你也可以产生一个专用线程来运行它并让一个actor管理它;在这种情况下,您可以以任何方式调用Thread.interrupt或Thread.stop,同时保持actor的响应。

我玩弄了一个让玩家管理启动和停止线程的想法。由于actor是管理潜在昂贵资源的好方法,因此演员确实是管理线程的好选择。这是我的实施:

/** Does the main work (i.e. training ML model). */
class WorkerThread(task: String, parent: ActorRef) extends Runnable {
  override def run(): Unit = try {
    while (true) {
      println(s"Doing $task...")
      Thread sleep 500
    }
  } catch {
    /* Since this thread may be interrupted at any time, we need to
       gracefully handle being interrupted. Since we have a handle to the
       actor that's managing us, we can send it a message telling it to
       finish up. */
    case _: InterruptedException => parent ! Worker.Message.FinishUp
  }
}

/** Manages starting and stopping the model training thread. */
class Worker extends Actor {
  private var thread: Thread = null

  override def receive: Receive = {
    case Worker.Message.DoTask(task) =>
      if (thread == null) {
        thread = new Thread(new WorkerThread(task, self))
        thread.start()
      }
    case Worker.Message.StopTask =>
      if (thread != null) thread.interrupt()
    case Worker.Message.FinishUp => println("Stopped task...")
  }
}

object Worker {
  sealed trait Message
  object Message {
    case class DoTask(task: String) extends Message
    case object StopTask extends Message
    case object FinishUp extends Message
  }
}

现在,我们可以随时使用其管理演员停止ML模型培训。事实证明,Future是一个错误的抽象层次,但Thread是正确的。