2

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?

Jatinshravan
  • 435
  • 3
  • 16
  • Is this not something where you can use asyncio? –  Jun 25 '15 at 05:07
  • Have a look at the examples for the [`asyncio`](https://docs.python.org/3/library/asyncio-task.html) module. In particular, you may want to use [coroutines](https://docs.python.org/3/library/asyncio-task.html#coroutine). – fferri Jun 25 '15 at 05:10
  • @Evert, I want my observers to be synchronous, and wait for a message to be published before they go ahead with execution. – Jatinshravan Jun 25 '15 at 05:13

2 Answers2

1

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.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
  • I want my observers to be synchronous. I want them to wait until there is data that has been published before going forward with the execution. I think that if I do everything in notify, wouldn't my observers be asynchronous? – Jatinshravan Jun 25 '15 at 05:21
  • 1
    Are you using threading? If not, everything is synchronous already. If you are using threading then check out the `Queue` -- your `notify` can submit the data to it, and your `wait` (or whatever you actually call it) can simply try to `get` from the `Queue` which will block until data shows up (without busy-waiting). As for `notify`, only the `Observable` calls it, so nothing in there runs until there is data available. – Ethan Furman Jun 25 '15 at 06:02
  • Yep, I was using threading. This solution works fine enough. Is there a problem if there are many instances of Queue.Queue() created when I have more than a few threads running? PS: Can you edit your answer with this information so I can accept it? – Jatinshravan Jun 26 '15 at 06:01
1

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().

fferri
  • 18,285
  • 5
  • 46
  • 95