Python signal between functions to remove busy waiting

1k views Asked by At

I'm just trying to implement a simple Observer pattern in Python and am running into an issue. This is my code:

class Observable:
    def __init__(self):
    self.__observers = []

    def register_observer(self, observer):
        self.__observers.append(observer)

    def notify_observers(self, data):
        for observer in self.__observers:
            observer.notify(self, data)

class Observer:

    def __init__(self, observable):
        observable.register_observer(self)
        self.data_present = False
        self.data = ''

    def notify(self, observable, data):
        self.data_present = True
        self.data = data

    def wait(self):
        while True:
            if not self.data_present:
                time.sleep(5)

            else:
                break

        return self.data

In here, I would like to eliminate the busy waiting in Observer.wait() (the line time.sleep(5). How can I perhaps signal to this function?

2

There are 2 answers

3
Ethan Furman On BEST ANSWER

You don't need to have the wait function at all -- just do what you need to in notify (process the data, log the data, mutilate/spindle/fold the data, whatever).

If you are using threading, check out the Queue.Queue class: it allows for multiple threads to synchronize on data availability without busy-waiting -- just have the notify method push data into a Queue, and wait can wait on it. Note that there is probably a more elegant solution using some of the other features found in the Threading module.

As a side note, you also don't need the double-underscore in self.__observers -- just self.observers is fine.

0
fferri On

You can use yield to suspend a function at one point, waiting for some value (blocked, without doing busy waiting).

def f():
    print('f: i am waiting for something...')
    c = yield
    print('f: i got %s' % c)
    yield None

On the other side you call .send(val) to resume its execution:

>>> g=f()
>>> next(g)
f: i am waiting for something...
>>> g.send(123)
f: i got 123
>>>

Note the additional yield None at the end of f() which prevents a StopIteration exception from being raised when you call .send().