简体   繁体   中英

Akka actor blocked by another actor running CPU-intensive job

I have an ActorSystem which has three actors, a MasterActor, a PrinterActor and a BlockerActor:

The PrinterActor simply sleeps for one second then prints out something:

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));
    }
}

The BlockerActor does some CPU-intensive work:

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);
     }
}

The MasterActor creates the above two actors then tells them to start working at the same time:

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());
    }
}

The main method instantiates an ActorSystem, creates a MasterActor under it then sends the actor a start message.

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);
    }
}

I expected the PrinterActor to finish fast but this was not the case. See the output below:

count is 5000000000000
elapsed time is 106856

It looks to me the PrinterActor didn't actually get a separate thread but was sharing a single thread with other two actors in the system. I'm having this impression because if I change the BlockerActor's implementation to:

public class BlockerActor extends UntypedActor {

    @Override
    public void onReceive(Object msg) throws Exception {
        Thread.sleep(60 * 1000);
    }
}

the PrinterActor would run much faster:

elapsed time is 1004

Note that I did not configure any dispatcher for my actors. So they should all be using the system's default dispatcher which has a pool of 3.0 (default parallelism-factor) * number of CPUs (8 cores in my machine) = 24 threads. I even tried to give the PrinterActor a PinnedDispatcher (dedicated thread) but still couldn't get the PrinterActor to speed up when my BlockerActor was working hard.

Now I'm really confused. Aren't you suppose to get some degree of parallelism when using actors like this? Is this an expected behavior of Akka or did I do anything wrong?

PS: I ran my program in Eclipse with Akka 2.3.6

The issue could be with Thread.sleep as there are platform/implementation related issues.

http://www.jwrapper.com/blog/worst-bug-ever-java-sleeps-3-orders-of-magnitude-too-long https://community.oracle.com/thread/2576985

I tried your example in Scala and did not experience any additional blocking for the PrintActor.

You can also add another printout for the "internal" execution time to verify if Thread.sleep is the problem.

elapsed time is 1005 (external)

elapsed time is 1007 (internal)

counter is 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()
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM