I am trying to understand the Actor and how the threads in pool work. I want an example to show that will used up the max threads configured, but it's not what I expected.
Here is my example:
using concurrent
using [java]java.lang::Thread
class ActorDemo {
Void main() {
pool := ActorPool { maxThreads = 4 }
actor := BusyActor(pool)
10.times |i| { actor.send("test$i") }
echo("${Thread.currentThread} all job have been submitted.")
pool.stop.join
echo("${Thread.currentThread}: done.")
}
}
const class BusyActor : Actor {
new make(ActorPool p) : super(p) {}
override Obj? receive(Obj? msg) {
busyDur := Duration.fromStr(Int.random(500..1000).toStr + "ms")
echo("${Thread.currentThread}: actor${this.hash} received msg: ${msg}. This task will take ${busyDur}")
Actor.sleep(busyDur) // pretend we are doing work here.
return null
}
}
Wen I run this, I expect that all 4 threads will be working and process the 10 msg as quickly as possible, but instead I see a serialized single thread running. Why is that?
Thread[main,5,main] all job have been submitted.
Thread[ThreadPool-Worker-0,5,main]: actor28031905 received msg: test0. This task will take 618ms
Thread[ThreadPool-Worker-0,5,main]: actor28031905 received msg: test1. This task will take 672ms
Thread[ThreadPool-Worker-0,5,main]: actor28031905 received msg: test2. This task will take 525ms
Thread[ThreadPool-Worker-0,5,main]: actor28031905 received msg: test3. This task will take 723ms
Thread[ThreadPool-Worker-0,5,main]: actor28031905 received msg: test4. This task will take 647ms
Thread[ThreadPool-Worker-0,5,main]: actor28031905 received msg: test5. This task will take 684ms
Thread[ThreadPool-Worker-0,5,main]: actor28031905 received msg: test6. This task will take 801ms
Thread[ThreadPool-Worker-0,5,main]: actor28031905 received msg: test7. This task will take 831ms
Thread[ThreadPool-Worker-0,5,main]: actor28031905 received msg: test8. This task will take 694ms
Thread[ThreadPool-Worker-0,5,main]: actor28031905 received msg: test9. This task will take 935ms
Thread[main,5,main]: done.
I went and experiment little more, and found that it will only have 4 threads running if I actually creates 4 instances of BusyActor that use the same pool. Are these expected behavior?
Thanks ~ Zemian
mslSun 18 Dec 2011
You're only creating a single Actor - so there'll only be one instance of it running in the thread pool (regardless of size).
If you do something like:
Void main() {
pool := ActorPool { maxThreads = 4 }
actors := [,]
4.times { actors += BusyActor(pool) } // Create 4 actors in the pool
actors.each |a| { 10.times |i| { a.send("test$i") } }
....
}
(not tested - YMMV) you should start seeing the behaviour you're expecting.
In short - think of an instance of an Actor as a single thread (but not really) and you're closer to the mark.
Martin
saltnlight5Mon 19 Dec 2011
@msl, Thanks for the note. I've already verified and said in my original post that if I create multiple instance of actors as you described will use up the threads. But it just seems little strange behvior to me, because now setting maxThreads to anything greater than number of actor instances is not in use.
saltnlight5 Sun 18 Dec 2011
Hi,
I am trying to understand the Actor and how the threads in pool work. I want an example to show that will used up the max threads configured, but it's not what I expected.
Here is my example:
Wen I run this, I expect that all 4 threads will be working and process the 10 msg as quickly as possible, but instead I see a serialized single thread running. Why is that?
I went and experiment little more, and found that it will only have 4 threads running if I actually creates 4 instances of
BusyActor
that use the same pool. Are these expected behavior?Thanks ~ Zemian
msl Sun 18 Dec 2011
You're only creating a single Actor - so there'll only be one instance of it running in the thread pool (regardless of size).
If you do something like:
(not tested - YMMV) you should start seeing the behaviour you're expecting.
In short - think of an instance of an Actor as a single thread (but not really) and you're closer to the mark.
Martin
saltnlight5 Mon 19 Dec 2011
@msl, Thanks for the note. I've already verified and said in my original post that if I create multiple instance of actors as you described will use up the threads. But it just seems little strange behvior to me, because now setting maxThreads to anything greater than number of actor instances is not in use.
If this is the way it ought to work, perhaps this can be better documented. Currently in http://fantom.org/doc/docLang/Actors.html, it says
Which I think it's a very confusing definition. From the experiment code, the
actor
instances are not created by ActorPool at all, but by user.The
actor
library is nice, but I still wish Fantom would have retained/readdThread
in sys pod.daitangio Mon 19 Dec 2011
Think in this terms:
Actors are good because they simplify concurrent programming.
For instance if you define only one "GatewayActor" you will got a syncronization point, without the need of definining a synchronized method.
On Erlang, it is quite the norm to have one master process which spawn subprocess (sub-actors) for managing requests