kquick / Thespian

Python Actor concurrency library
MIT License
189 stars 24 forks source link

An example where multiprocUDPBase works but multiprocTCPBase doesn't #45

Open yoavkantor opened 5 years ago

yoavkantor commented 5 years ago

============code============= from thespian.actors import *

class BusyActor(Actor): def receiveMessage(self, message, sender): i = 0 while True: i += 1 if i == 1000000: i = 0

a1 = ActorSystem("multiprocTCPBase").createActor(BusyActor) a2 = ActorSystem("multiprocTCPBase").createActor(BusyActor) ActorSystem().ask(a1, 'hi', 1) ActorSystem().ask(a2, 'hi', 1)

time.sleep(600) ============code=============

This fails with: thespian.actors.InvalidActorSpecification: Invalid Actor Specification: <class 'main.BusyActor'> (module 'main' has no attribute 'BusyActor')

However using "multiprocUDPBase" ActorSystems works. Any idea why? Thanks!

kquick commented 5 years ago

When you start one of the 'multiproc...Base' actor systems, it creates the actors as separate processes, including a 'MultiProcAdmin' actor that manages the system for you. The current process is intended to be a client of the actor system, not the owner of the actor system; the actor system is intended to persist beyond the existence of the current process, until it is explicitly shutdown. The MultiProcAdmin acts as the owner of the actor system in this configuration.

One subtle side effect of that is that when you create actors, the local process simply sends a message to the MultiProcAdmin, and it is the latter that actually creates the actors. However, the MultiProcAdmin can only create actors from source that it knowns about, which means either the version of the source that it was started with, or a loaded source (https://thespianpy.com/doc/using.html#hH-955ec990-a267-4d08-8f4d-5f980c777173).

I suspect that you initially created a multiprocTCPBase system with code that did not define BusyActor, which is why you got the failure message above, but that when you tried the multiprocUDPBase you had already defined the BusyActor by that point, which is why it worked.

My advice for simple exploratory programs like the above, where you do not want to have the actor system persist beyond the run of the program is to use the following pattern:

asys = ActorSystem("multiprocTCPBase")
try:
    ... do stuff with asys ...
finally:
    asys.shutdown()

This will stop the actor system at the end of the program, so that if you make changes and re-run it then the new actor system will see the new code.

I would also recommend the multisystem example tutorial (https://github.com/kquick/Thespian/tree/master/examples/multi_system) which will discuss this in more detail. If you want to use loadable sources, the Thespian Director (https://thespianpy.com/doc/director.html) can be useful (although not required).

yoavkantor commented 5 years ago

Thanks a lot for the thorough explanation. Hope it will help others as well :)