Akka演员被另一个运行CPU密集型工作的演员阻止

时间:2014-11-06 23:02:13

标签: java multithreading akka blocking

我有一个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运行我的程序

1 个答案:

答案 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()
  }
}