I am trying to design a dispatcher-worker actor pattern for Scala using the standard scala.actors package.
The dispatcher receives work from a java.util.concurrent.LinkedBlockingQueue and sends it out to worker actors to be processed. When all of the work is done the dispatcher should tell each of the workers to quit and then it should also quit. Here is the code I came up with, but it hangs when all of the work is done (I think there are pending 'GiveMeWork messages in the dispatcher’s queue):
import java.util.concurrent.LinkedBlockingQueue
import scala.actors.Actor
object Dispatcher
extends Actor {
println("Dispatcher created")
def act() {
val workers = (1 to 4).map(id => (new Worker(id)).start())
loop {
react {
case 'GiveMeWork =>
// println("Worker asked for work")
val (time, i) = workQueue.take()
if (time == 0) {
println("Quitting time")
workers.foreach(_ !? 0L)
} else {
println("Arrival at dispatcher: i: " + i + " dispatch time: " +
time + ", elapsed: " + (System.nanoTime() - time))
sender ! time
}
case 'Quit =>
println("Told to quit")
sender ! 'OffDuty
exit()
}
}
}
}
class Worker(id: Int)
extends Actor {
println("Worker(" + id + ") created")
var jobs = 0
def act() {
Dispatcher ! 'GiveMeWork
loop {
react {
case time: Long =>
if (time == 0) {
println("Worker(" + id + ") completed " + jobs + " jobs")
sender ! 'OffDuty
exit()
} else {
println("Arrival at worker(" + id + "): dispatch time: " +
time + ", elapsed: " + (System.nanoTime() - time))
Thread.sleep(id)
jobs += 1
Dispatcher ! 'GiveMeWork
}
}
}
}
}
val workQueue = new LinkedBlockingQueue[(Long, Int)](1000)
Dispatcher.start()
for (i <- 0 until 5000) {
Thread.sleep(1)
workQueue.put((System.nanoTime(), i))
}
workQueue.put((0L, 0))
println("Telling Dispatcher to quit")
Dispatcher !? 'Quit
There is a race:
All the work is done, including
workQueue.put((0L, 0)), so it will wait forever.It’s a bad idea to use different types of concurrency simultaneously.
Dispatcher can inform task source about task limit: