tags:

views:

302

answers:

2

I'm sure this is a very simple question, but embarrassed to say I can't get my head around it:

I have a list of values in Scala. I would like to use use actors to make some (external) calls with each value, in parallel.

I would like to wait until all values have been processed, and then proceed.

There's no shared values being modified.

Could anyone advise?

Thanks

+3  A: 

Create workers and ask them for futures using !!; then read off the results (which will be calculated and come in in parallel as they're ready; you can then use them). For example:

object Example {
  import scala.actors._
  class Worker extends Actor {
    def act() { Actor.loop { react {
      case s: String => reply(s.length)
      case _ => exit()
    }}}
  }
  def main(args: Array[String]) {
    val arguments = args.toList
    val workers = arguments.map(_ => (new Worker).start)
    val futures = for ((w,a) <- workers zip arguments) yield w !! a
    val results = futures.map(f => f() match {
      case i: Int => i
      case _ => throw new Exception("Whoops--didn't expect to get that!")
    })
    println(results)
    workers.foreach(_ ! None)
  }
}

This does a very inexpensive computation--calculating the length of a string--but you can put something expensive there to make sure it really does happen in parallel (the last thing that case of the act block should be to reply with the answer). Note that we also include a case for the worker to shut itself down, and when we're all done, we tell the workers to shut down. (In this case, any non-string shuts down the worker.)

And we can try this out to make sure it works:

scala> Example.main(Array("This","is","a","test"))
List(4, 2, 1, 4)
Rex Kerr
Thanks so much - I seemed to have completely missed mention of futures and usage of !! before this.
7zark7
I should point out that I was perhaps being too literal with using actors: if you're not going to reuse them, Daniel's answer is even shorter and more direct to get to the parallel computation that you requested. (I.e. use futures without actors.)
Rex Kerr
+7  A: 

There's an actor-using class in Scala that's made precisely for this kind of problem: Futures. This problem would be solved like this:

// This assigns futures that will execute in parallel
// In the example, the computation is performed by the "process" function
val tasks = list map (value => scala.actors.Futures.future { process(value) })

// The result of a future may be extracted with the apply() method, which
// will block if the result is not ready.
// Since we do want to block until all results are ready, we can call apply()
// directly instead of using a method such as Futures.awaitAll()
val results = tasks map (future => future.apply())

There you go. Just that.

Daniel
This is too simple, there must be something wrong with it ;-)Looks great, thanks again.
7zark7
@7zark7 Yeah, I felt that way too. :-) It compiles, though, and I don't think I made any particular mistake. Make sure `process` doesn't throw exceptions, and be aware that if you are going to do I/O, it might not work as well as you expect, as the running futures will just block the threads waiting.
Daniel
It works. In fact, I tested it to make sure it multitasks as efficiently as does the actor style (I was curious, since I hadn't done that before or looked into the `Futures` source code), and it does.
Rex Kerr