我最近在学习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监视我的线程信息,我得到:
您可能会发现只有两名调度员在睡觉(PrintActor
会让这种情况发生)。其他人正在等待,这意味着他们可用于新的消息传递。
我已经阅读了有关在Actor(page)中阻止操作的答案。引用“Dispatchers,实际上是线程池。分离两个保证缓慢,阻塞操作不会使另一个挨饿。这种方法通常被称为批量标题,因为这个想法是如果应用程序的一部分失败,其余部分仍然保持响应。“
默认调度程序是否需要一些调度程序来阻止操作?这样即使有很多阻塞操作要求调度员,系统也可以处理消息。
可以复制Akka文件中的实验吗?我的配置有问题吗。
感谢您的建议。祝福。
答案 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
...