阻止Actor中的操作不占用所有默认调度程序

时间:2017-08-04 16:12:36

标签: scala akka actor blocking akka-dispatcher

我最近在学习Akka演员。我读了演员的调度员文件。我很好奇演员中的阻塞操作。文档中的最后一个topic描述了如何解决问题。我正在尝试在文档中重现示例实验。

这是我的代码:

a = df.loc[:,0]

我只是使用默认调度程序创建for i in range(df.columns.size): dfs[i] = df.loc[:, i] ,所有actor都依赖于这些调度程序。 package dispatcher import akka.actor.{ActorSystem, Props} import com.typesafe.config.ConfigFactory object Main extends App{ var config = ConfigFactory.parseString( """ |my-dispatcher{ |type = Dispatcher | |executor = "fork-join-executor" | |fork-join-executor{ |fixed-pool-size = 32 |} |throughput = 1 |} """.stripMargin) // val system = ActorSystem("block", ConfigFactory.load("/Users/jiexray/IdeaProjects/ActorDemo/application.conf")) val system = ActorSystem("block") val actor1 = system.actorOf(Props(new BlockingFutureActor())) val actor2 = system.actorOf(Props(new PrintActor())) for(i <- 1 to 1000){ actor1 ! i actor2 ! i } } package dispatcher import akka.actor.Actor import scala.concurrent.{ExecutionContext, Future} class BlockingFutureActor extends Actor{ override def receive: Receive = { case i: Int => Thread.sleep(5000) implicit val excutionContext: ExecutionContext = context.dispatcher Future { Thread.sleep(5000) println(s"Blocking future finished ${i}") } } } package dispatcher import akka.actor.Actor class PrintActor extends Actor{ override def receive: Receive = { case i: Int => println(s"PrintActor: ${i}") } } 具有封装在ActorSystem中的阻止操作。 BlockingFutureActor只是立即打印一个数字。

在文档的解释中,默认调度程序将被Future中的PrintActor占用,这会导致Future的邮件被阻止。应用程序卡在某处,如:

BlockingFutureActor

不幸的是,我的代码没有被阻止。 PrintActor的所有输出都会顺利显示。但> PrintActor: 44 > PrintActor: 45 的输出显示出像挤牙膏一样。我尝试通过Intellij的Debug监视我的线程信息,我得到: thread monitoring

您可能会发现只有两名调度员在睡觉(PrintActor会让这种情况发生)。其他人正在等待,这意味着他们可用于新的消息传递。

我已经阅读了有关在Actor(page)中阻止操作的答案。引用“Dispatchers,实际上是线程池。分离两个保证缓慢,阻塞操作不会使另一个挨饿。这种方法通常被称为批量标题,因为这个想法是如果应用程序的一部分失败,其余部分仍然保持响应。“

默认调度程序是否需要一些调度程序来阻止操作?这样即使有很多阻塞操作要求调度员,系统也可以处理消息。

可以复制Akka文件中的实验吗?我的配置有问题吗。

感谢您的建议。祝福。

1 个答案:

答案 0 :(得分:2)

您在PrintActor的任何打印语句之前看到来自BlockingFutureActor的所有1000个打印语句的原因是Thread.sleep&#39中的第一个BlockingFutureActor来电; s receive阻止。这个Thread.sleep是您的代码与官方文档中的示例之间的主要区别:

override def receive: Receive = {
  case i: Int =>
    Thread.sleep(5000) // <----- this call is not in the example in the official docs
    implicit val excutionContext: ExecutionContext = context.dispatcher
    Future {
      ...
    }
}

请记住,actor一次处理一条消息。 Thread.sleep(5000)基本上模拟了至少需要五秒钟才能处理的消息。 BlockingFutureActor不会处理另一条消息,直到它完成处理当前消息,即使其邮箱中有数百条消息。虽然BlockingFutureActor正在处理值Int的第一条1消息,但PrintActor已经处理完所有已发送给它的1000条消息。为了更清楚,请添加println语句:

override def receive: Receive = {
  case i: Int =>
    println(s"Entering BlockingFutureActor's receive: $i") // <-----
    Thread.sleep(5000)
    implicit val excutionContext: ExecutionContext = context.dispatcher
    Future {
      ...
    }
}

运行程序时的示例输出:

Entering BlockingFutureActor's receive: 1
PrintActor: 1
PrintActor: 2
PrintActor: 3
...
PrintActor: 1000
Entering BlockingFutureActor's receive: 2
Entering BlockingFutureActor's receive: 3
Blocking future finished 1
...

正如您所看到的,当BlockingFutureActor实际开始处理邮件2时,PrintActor已经在所有1000封邮件中流失。

如果您删除了第一个Thread.sleep,那么您会更快地看到从BlockingFutureActor邮箱中出列的邮件,因为正在进行工作&#34;委派&# 34;到Future。创建Future后,actor会从其邮箱中获取下一条消息,而无需等待Future完成。下面是没有第一个Thread.sleep的示例输出(每次运行时它都不会完全相同):

Entering BlockingFutureActor's receive: 1
PrintActor: 1
PrintActor: 2
...
PrintActor: 84
PrintActor: 85
Entering BlockingFutureActor's receive: 2
Entering BlockingFutureActor's receive: 3
Entering BlockingFutureActor's receive: 4
Entering BlockingFutureActor's receive: 5
PrintActor: 86
PrintActor: 87
...