First I need an oscillator process, whose main purpose is to generate events at predefined intervals of time. Any other processes that require timed execution can listen for the events and react accordingly.
The oscillator process uses the time. sleep() function to measure intervals between the events. Because there is not much else to do apart from setting and resetting the event, the timer is pretty accurate. Listing 9-7 shows the code that implements the oscillator class.
Listing 9-7. The oscillator class generates events at defined intervals. class Oscillator(multiprocessing.Process):
self.period = period self.event = event super(Oscillator, self).__init__()
self.event.clear() time.sleep(self.period) self.event.set() except KeyboardInterrupt: pass
The oscillator class accepts a proxy object, which is referenced in the example as the event variable. This proxy object is an instance returned by the multiprocessing.Manager class. The Manager class is a mechanism of sharing state and data between different processes and supports other data types as well, such as: list, dict, NameSpace, Lock, RLock, Semaphore, Condition, Event, Oueue, Value, and Array. Apart from the list, dict, and NameSpace, all other types are clones of the corresponding primitives in the threading library.
Let's define a simple class that will listen to the events and perform some actions when they are received. The code in Listing 9-8 simply prints the current time.
Listing 9-8. The Scheduler class listens to periodic events. class Scheduler(multiprocessing.Process):
self.event = event super(Scheduler, self).__init__()
self.event.wait() print datetime.now() except KeyboardInterrupt: pass
Now let's see how it all comes together. In the main process code in Listing 9-9 I am creating an instance of the Manager class. I will then use it to return a proxy to the Event instance. The same object will be passed to both the Oscillator and the Scheduler processes. The Oscillator will set and clear the event state, and the Scheduler will wait for the event to clear before it prints the time and goes back to the wait state again.
Listing 9-9. Passing a shared event object to the two processes mgr = multiprocessing.Manager()
while len(multiprocessing.active_children()) != 0: time.sleep(1) except KeyboardInterrupt: o.terminate() s.terminate() o.join() s.join()
If you run this code, you'll get the output generated every minute. You can use as many "subscriber" objects as you need here, all waiting for the event generated by the Oscillator instance.
2010-02-28 18:35:09.243200 2010-02-28 18:36:09.244793 2010-02-28 18:37:09.246509 2010-02-28 18:38:09.248229 2010-02-28 18:39:09.249935 2010-02-28 18:40:09.251436 2010-02-28 18:41:09.253154
It is important to note that this implementation, although quite accurate, is not ideal and the interval is actually slightly longer than the predefined 60 seconds. This is because some time is spent resetting the event object. However, given the interval size (60 seconds), this error is really negligible (approximately 2000 milliseconds) and is only approximately 0.003% of the total oscillation period. For a simple scheduling system this is acceptable.
Was this article helpful?