我有一个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
。但我不知道如何融入这个架构。希望有人帮助我。
答案 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是正确的。