Wednesday, November 11, 2009

Introduction to Actors

The support for Actors is one of the features that attracts many new developers to Scala. It is not the only concurrancy construct in Scala but it is one of the most recognized.

An actor is essentially an active object. Instead of calling methods on the actor messages are passed to the actor and the message will be handled in a thread. Resources about actors can be found all over the internet a couple pages to look at are:
This is a big topic about how threads are scheduled and so on but for now we will write a simple little program that downloads several webpages in parallel.
  1. scala> import scala.actors.Actor
  2. import scala.actors.Actor
  3. scala> import Actor._
  4. import scala.actors.Actor._
  5. scala>  val collector = actor {
  6.      |   var count = 3
  7.      |   var data = ""
  8.      |   loop {
  9.      |   react { 
  10.      |    case payload:String => {
  11.      |      reply ("thank you")
  12.      |      data += payload + "\n\n"
  13.      |      count -= 1
  14.      |      if (count == 0) {
  15.      |        println (data)
  16.      |        exit()
  17.      |     }
  18.      |    }
  19.      |  }
  20.      | }
  21.      | }
  22. collector: scala.actors.Actor = scala.actors.Actor$$anon$1@2bbef4c6
  23. scala> import scala.io.Source
  24. import scala.io.Source
  25. scala> class Downloader(url:Stringextends Actor {
  26.      |  def act = {
  27.      |   val source = Source.fromURL(new java.net.URL(url))
  28.      |   val data = source.getLines.mkString("\n")
  29.      |   collector ! data
  30.      |   receive { case s => println("Done with "+url) }
  31.      |  }
  32.      | }
  33. defined class Downloader
  34. scala> List("http:/daily-scala.blogspot.com/2009/11/using-objects-as-functions.html",
  35.      |      "http:/daily-scala.blogspot.com/2009/10/boolean-extractors.html",
  36.      |      "http:/daily-scala.blogspot.com/2009/08/java-vs-scala-control-structures.html",
  37.      |      "http:/www.blogger.com/profile/07600430363435495915")
  38. res0: List[java.lang.String] = List(http:/daily-scala.blogspot.com/2009/11/using-objects-as-functions.html, http:/daily-scala.blogspot.com/2009/10/boolean-extractors.html, http:/daily-scala.blogspot.com/2009/08/java-vs-scala-control-structures.html, http:/www.blogger.com/profile/07600430363435495915)
  39. scala> for (url <- res0) { 
  40.      | new Downloader(url).start
  41.      | }
  42. [snip... lots of output]

Update:
If this program is put into a file and executed it will not finish is because the program exits. What is happening is there are 6 "actors" the main thread, collector and the 4 Downloaders. The main thread completes and shutdown the system taking all the Actors with it.

Just add link(collector) as the last line to make the main trhead wait for collector.

Also important to realize is that in Scala 2.7 an actor is lightweight. IE the application can exit while actors are still alive. In Scala 2.8 that is changed. For Scala 2.7 semantics you must use a DaemonActor instead of Actor.

2 comments:

  1. When I run this script on the command line, it works fine. But when I put write it in a file and execute this file with scala, it does not display anything (2.7.7).
    Do you have the same problem ?
    I have modified the end with:
    val urls = List(.....)
    for( url <- urls) { new Downloader(url).start }

    ReplyDelete
  2. The reason it does not finish is because the program exits. What is happening is there are 6 "actors" the main thread, collector and the 4 Downloaders. The main thread completes and shutdown the system.

    Just add link(collector). This will make the main thread wait for collector.

    Also important to realize is that in Scala 2.7 an actor is lightweight. IE the application can exit while actors are still alive. In Scala 2.8 that is changed. For Scala 2.7 semantics you must use a DaemonActor instead of Actor.

    ReplyDelete