我有一个ActorSystem,它有三个actor,一个MasterActor,一个PrinterActor和一个BlockerActor:
PrinterActor只是睡了一秒钟然后打印出来的东西:
public class PrinterActor extends UntypedActor {
@Override
public void onReceive(Object msg) throws Exception {
Thread.sleep(1000);
System.out.println("elapsed time is " + (System.currentTimeMillis()-(Long)msg));
}
}
BlockerActor执行一些CPU密集型工作:
public class BlockerActor extends UntypedActor {
@Override
public void onReceive(Object msg) throws Exception {
long count=0;
for (int i=0; i<5000; i++) {
for (int j=0; j<1000000000; j++) {
count++;
}
}
System.out.println("count is " + count);
}
}
MasterActor创建上面两个actor然后告诉他们同时开始工作:
public class MasterActor extends UntypedActor {
@Override
public void onReceive(Object msg) throws Exception {
ActorRef printer = getContext().actorOf(Props.create(PrinterActor.class), "printerActor");
ActorRef blocker = getContext().actorOf(Props.create(BlockerActor.class), "blockerActor");
blocker.tell("start", getSelf());
printer.tell(System.currentTimeMillis(), getSelf());
}
}
main方法实例化一个ActorSystem,在其下创建一个MasterActor,然后向actor发送一条开始消息。
public class App {
public static void main( String[] args ) {
ActorSystem actorSystem = ActorSystem.create("Test");
ActorRef master = actorSystem.actorOf(Props.create(MasterActor.class), "masterActor");
master.tell("start", null);
}
}
我希望PrinterActor能够快速完成但事实并非如此。请参阅以下输出:
count is 5000000000000
elapsed time is 106856
在我看来,PrinterActor实际上并没有获得单独的线程,而是与系统中的其他两个actor共享一个线程。我有这种印象,因为如果我将BlockerActor的实现更改为:
public class BlockerActor extends UntypedActor {
@Override
public void onReceive(Object msg) throws Exception {
Thread.sleep(60 * 1000);
}
}
PrinterActor运行得更快:
elapsed time is 1004
请注意,我没有为我的演员配置任何调度程序。所以他们都应该使用系统的默认调度程序,它具有3.0(默认并行因子)* CPU数量(我的机器中有8个核心)= 24个线程。我甚至试图给PrinterActor一个PinnedDispatcher(专用线程)但是当我的BlockerActor努力工作时仍然无法使PrinterActor加速。
现在我真的很困惑。你不是想在使用这样的演员时获得某种程度的并行性吗?这是Akka的预期行为还是我做错了什么?
PS:我在Eclipse中用Akka 2.3.6运行我的程序
答案 0 :(得分:1)
问题可能在于Thread.sleep,因为存在与平台/实现相关的问题。
http://www.jwrapper.com/blog/worst-bug-ever-java-sleeps-3-orders-of-magnitude-too-long https://community.oracle.com/thread/2576985
我在Scala中尝试了您的示例,并且没有遇到PrintActor的任何其他阻止。
您还可以为“内部”执行时间添加另一个打印输出,以验证Thread.sleep是否是问题。
已用时间为1005(外部)
已用时间为1007(内部)
计数器是5000000000
package Actors
import akka.actor.{ActorSystem, Actor, Props}
class PrintActor extends Actor {
override def receive = {
case externalStartTime: Long =>
val internalStartTime = System.currentTimeMillis()
Thread.sleep(1000)
println(s"elapsed time is ${System.currentTimeMillis() - externalStartTime} (external)")
println(s"elapsed time is ${System.currentTimeMillis() - internalStartTime} (internal)")
case _ => throw new IllegalArgumentException
}
}
class BlockerActor extends Actor {
override def receive = {
case "start" =>
var counter = 0L
for (i <- 1 to 5000) {
for (j <- 1 to 1000000) { // 1000000000
counter += 1
}
}
println(s"counter is $counter")
case _ => throw new IllegalArgumentException
}
}
class MasterActor extends Actor {
override def receive = {
case "start" =>
val printer = context.actorOf(Props[PrintActor], "printerActor")
val blocker = context.actorOf(Props[BlockerActor], "blockerActor")
blocker.tell("start", self)
printer.tell(System.currentTimeMillis(), self)
case _ => throw new IllegalArgumentException
}
}
object App {
def main (args: Array[String]) {
val actorSystem = ActorSystem("Test")
val master = actorSystem.actorOf(Props[MasterActor], "masterActor")
master.tell("start", null)
actorSystem.shutdown()
}
}