47

Are there scenarios where polling for events would be better than using the observer pattern? I have a fear of using polling and would only start using it if someone gave me a good scenario. All I can think of is how the observer pattern is better than polling. Conside this scenario:

You are programming a car simulator. The car is an object. As soon as the car turns on, you want to play a "vroom vroom" sound clip.

You can model this in two ways:

Polling: Poll the car object every second to see if it's on. When it's on, play the sound clip.

Observer pattern: Make the car the Subject of the observer pattern. Have it publish the "on" event to all observers when itself turns on. Create a new sound object that listens to the car. Have it implement the "on" callback, which plays the sound clip.

In this case, I think the observer pattern wins. Firstly, polling is more processor intensive. Secondly, the sound clip does not immediately fire when the car turns on. There can be up to a 1 second gap because of the polling period.

3
  • I can think of almost no scenario. Observer pattern is what actually maps to real world and real life. Thus I think no scenario would ever justify not using it. Aug 22, 2011 at 7:43
  • Are you talking about user interface events, or events in general? Aug 22, 2011 at 11:17
  • 4
    Your example doesn't remove the polling/observing problem. You've simply passed it to a lower level. Your program still needs to figure out if the car is on or not by some mechanism.
    – Dunk
    Aug 22, 2011 at 15:29

7 Answers 7

61

Imagine you want to get notified about every engine cycle, e.g. to display an RPM measurement to the driver.

Observer pattern: The engine publishes an "engine cycle" event to all observers for each cycle. Create a listener that counts events and updates the RPM display.

Polling: The RPM display asks the engine at regular intervals for an engine cycle counter, and updates the RPM display accordingly.

In this case, the observer pattern would probably loose: the engine cycle is a high-frequency, high-priority process, you don't want to delay or stall that process just to update a display. You also don't want to thrash the thread pool with engine cycle events.


PS: I also use the polling pattern frequently in distributed programming:

Observer pattern: Process A sends a message to process B that says "each time an event E occurs, send a message to Process A".

Polling pattern: Process A regularly sends a message to process B that says "if you event E occured since the last time I've polled, send me a message now".

The polling pattern produces a bit more network load. But the observer pattern has downsides, too:

  • If process A crashes, it will never unsubscribe, and process B will try to send notifications to it for all eternity, unless it can reliably detect remote process failures (not an easy thing to do)
  • If event E is very frequent and/or the notifications carry a lot of data, then process A might get more event notifications than it can handle. With the polling pattern, it can just throttle the polling.
  • In the observer pattern, high load can cause "ripples" through the whole system. If you use blocking sockets, these ripples can go both ways.
8
  • 1
    Good point. Sometimes it's better too poll for the sake of performance.
    – Falcon
    Aug 22, 2011 at 8:12
  • 1
    Expected number of observers is also a consideration. When you expect a large number of observers, updating them all from the observed can become a performance bottleneck. Much easier to then just write a value somewhere and have the "observers" check that value when they need it. Aug 22, 2011 at 8:25
  • 1
    "unless it can reliably detect remote process failures (not an easy thing to do)" ... except by polling ;P. Thus the best design there is to minimise the "nothing has changed" response as much as possible. +1, good answer.
    – pdr
    Aug 22, 2011 at 9:24
  • 2
    @Jojo: You could, yes, but then you are putting policy that should belong in the display into the RPM counter. Perhaps the user occasionally wants to have a highly precise RPM display.
    – Zan Lynx
    Aug 22, 2011 at 18:56
  • 2
    @JoJo: Publishing each 100th event is a hack. It only works well if the event frequency is always in the right range, if processing the event doesn't take too long for the engine, if all subscribers need comparable accuracy. And it takes one modulus operation per RPM, which (assuming a few thousand RPM) is a lot more work for the CPU than a few polling operations per second.
    – nikie
    Aug 22, 2011 at 19:06
7

Polling is better if the polling process runs considerably slower than the things it polls. If you are writing events to a database, it's often better to poll all your event producers, collect all the events that have occurred since the last poll, then write them in a single transaction. If you tried to write every event as it occurred, you might not be able to keep up and eventually would have problems when your input queues filled up. It also makes more sense in loosely-coupled distributed systems, where latency is high or connection set-up and tear-down are expensive. I find polling systems easier to write and understand, but in most situations observers or event-driven consumers seem to offer better performance (in my experience).

7

Polling is a lot easier to get to work over a network when connections may fail, servers may go done etc. Remember at the end of the day a TCP socket needs “polling” keep-a-live messages otherwise the server will assume the client has gone away.

Polling is also good when you wish to keep a UI updated, but the underlying objects change very fast, there is no point update the UI more than a few times a second in most apps.

Provided the server can response “no change” at very low cost and you don’t poll too often and you don’t have 1000s of clients polling, then polling works very well in real life.

However for “in memory” cases, I default to using the observer pattern as it is normally the least work.

5

Polling has some disadvantages, you basically stated them already in your question.

However, it can be a better solution, when you want to truly decouple the observable from any observers. But sometimes it might be better to use an observable wrapper for the object to be observed in such cases.

I'd only use polling when the observable can't be observed with object interactions, which is frequently the case when querying databases for example, where you can't have callbacks. Another issue might be multithreading, where it's often times safer to poll and process messages rather than invoking objects directly, to avoid concurrency issues.

1
  • I'm not quite sure why you believe polling is safer for multi threading. In most cases, this won't be the case. When poll handler receives a poll request, it had to figure out the state of the polled object, if the object is in the middle of updating, then it's not safe for the poll handler. In listener scenario, you only get notifications if the pusher is in consistent state, so you can avoid most synchronization issues in the polled object.
    – Lie Ryan
    Feb 10, 2017 at 13:38
4

For a good example of when polling takes over from notification, look at operating system networking stacks.

It was a big deal for Linux when the networking stack enabled NAPI, a networking API that allowed the drivers to switch from an interrupt mode (notification) to a polling mode.

With multiple gigabit Ethernet interfaces the interrupts would often overload the CPU, causing the system to run slower than it ought to. With polling the network cards collect packets in buffers until polled or the cards even write the packets into memory via DMA. Then when the operating system is ready it polls the card for all of its data and does the standard TCP/IP processing.

The polling mode allows the CPU to collect Ethernet data at its maximum processing rate without useless interrupt load. The interrupt mode allows the CPU to idle between packets when work is not so busy.

The secret is when to switch from one mode to the other. Each mode has advantages and should be used in the proper place.

2

I love polling! Do I? Yes! Do I? Yes! Do I? Yes! Do I still? Yes! What about now? Yes!

As others have mentioned, it can be incredibly inefficient if you're polling only to get back the same unchanged state over and over. Such is a recipe for burning up CPU cycles and significantly shortening battery life on mobile devices. Of course it's not wasteful if you're getting back a new and meaningful state every single time at a rate no faster than desired.

But the main reason I love polling is because of its simplicity and predictable nature. You can trace through the code and easily see when and where things are going to happen, and in what thread. If, theoretically, we lived in a world where polling was a negligible waste (albeit the reality being far from it), then I believe it would simplify maintaining code a tremendous deal. And that's the benefit of polling and pulling as I see if we could disregard performance, even though we shouldn't in this case.

When I started programming in the DOS era, my little games revolved around polling. I copied some assembly code from a book which I barely understood relating to keyboard interrupts and made it store a buffer of keyboard states, at which point my main loop was always polling. Is the up key down? Nope. Is the up key down? Nope. How about now? Nope. Now? Yes. Okay, move the player.

And while incredibly wasteful, I found that so much easier to reason about compared to these days of multi-tasking and event-driven programming. I knew exactly when and where things would occur at all times and it was easier to keep frame rates stable and predictable without hiccups.

So since then I've always been trying to find a way to get some of the benefits and predictability of that without actually burning up CPU cycles, like using condition variables to notify threads to wake up at which point they can pull the new state, do their thing, and go back to sleep waiting to be notified again.

And somehow I find event queues a lot easier to work with at least than observer patterns, even though they still don't make it so easy to predict where you'll end up going or what will end up happening. They at least centralize the event handling control flow to a few key areas in the system and always handling those events in the same thread instead of bouncing from one function to somewhere completely remote and unexpected all of a sudden outside of a central event handling thread. So the dichotomy doesn't always have to be between observers and polling. Event queues are kind of a middle ground there.

But yeah, somehow I find it so much easier to reason about systems that do things that are analogically closer to the kind of predictable control flows I used to have when I was polling ages ago, while just counteracting the tendency for the work to occur at times when no state changes have occurred. So there's that benefit if you can do it in a way that isn't burning CPU cycles needlessly like with condition variables.

Homogeneous Loops

All right, I got a great comment from Josh Caswell that pointed out some goofiness in my answer:

"like using condition variables to notify threads to wake up" Sounds like an event-based/observer arrangement, not polling

Technically the condition variable itself is applying the observer pattern to wake up/notify threads, so to call that "polling" would probably be incredibly misleading. But I find it provides a similar benefit I found as polling from the DOS days (just in terms of control flow and predictability). I'll try to explain it better.

What I found appealing back in those days was that you could look at a section of code or be tracing through it and say, "Okay, this entire section is dedicated to handling keyboard events. Nothing else is going to happen in this section of code. And I know exactly what's going to happen before, and I know exactly what's going to happen after (physics and rendering, e.g.)." The polling of the keyboard states gave you that kind of centralization of control flow as far as handling what should go on in response to this external event. We didn't respond to this external event immediately. We responded to it at our convenience.

When we use a push-based system based on an Observer pattern, we often lose those benefits. A control might get resized which triggers a resize event. When we trace through it, we find we're inside exotic control which does a lot of custom things in its resizing which triggers more events. We end up being completely surprised tracing into all these cascading events as to where we end up in the system. Furthermore, we might find that all of this doesn't even consistently occur in any given thread because thread A might resize a control here while thread B also resizes a control later on. So I always found this very difficult to reason about given how difficult it is to predict where everything happens as well as what will happen.

The event queue is a little bit simpler to me to reason about because it simplifies where all these things happen at least at a thread level. However, many disparate things could be happening. An event queue could contain an eclectic mixture of events to process, and each one could still surprise us as to what cascade of events that occurred, the order in which they were processed, and how we end up bouncing all over the place in the codebase.

What I'm considering "closest" to polling would not use an event queue but would defer a very homogeneous type of processing. A PaintSystem might be alerted through a condition variable that there's painting work to do to repaint certain grid cells of a window, at which point it does a simple sequential loop through the cells and repaints everything inside it in proper z-order. There might be one level of calling indirection/dynamic dispatch here to trigger the paint events in each widget residing in a cell that needs to be repainted, but that's it -- just one layer of indirect calls. The condition variable uses the observer pattern to alert the PaintSystem that it has work to do, but it doesn't specify anything more than that, and the PaintSystem is devoted to one uniform, very homogeneous task at that point. When we're debugging and tracing through the PaintSystem's code, we know that nothing else will happen except painting.

So it's mostly about getting the system down to where you have these things performing homogeneous loops over data applying a very singular responsibility over it instead of non-homogeneous loops over disparate types of data performing numerous responsibilities as we might get with event queue processing.

We're aiming for this type of thing:

when there's work to do:
   for each thing:
       apply a very specific and uniform operation to the thing

As opposed to:

when one specific event happens:
    do something with relevant thing
in relevant thing's event:
    do some more things
in thing1's triggered by thing's event:
    do some more things
in thing2's event triggerd by thing's event:
    do some more things:
in thing3's event triggered by thing2's event:
    do some more things
in thing4's event triggered by thing1's event:
    cause a side effect which shouldn't be happening
    in this order or from this thread.

And so forth. And it doesn't have to be one thread per task. One thread might apply layouts (resizing/repositioning) logic for GUI controls and repaint them, but it might not handle keyboard or mouse clicks. So you could look at this as just improving the homogeneity of an event queue. But we don't have to use an event queue and interleave resizing and painting functions either. We can do like:

in thread dedicated to layout and painting:
    when there's work to do:
         for each widget that needs resizing/reposition:
              resize/reposition thing to target size/position
              mark appropriate grid cells as needing repainting
         for each grid cell that needs repainting:
              repaint cell
         go back to sleep

So the above approach just uses a condition variable to notify the thread when there's work to do, but it doesn't interleave different types of events (resize in one loop, paint in another loop, not a mixture of both) and it doesn't bother to communicate what the work is exactly that needs to be done (the thread "discovers" that upon waking up by looking at system-wide states of the ECS). Each loop it performs is then very homogeneous in nature, making it easy to reason about the order in which everything happens.

I'm not sure what to call this type of approach. I haven't seen other GUI engines do this and it's kind of my own exotic approach to mine. But before when I tried to implement multithreaded GUI frameworks using observers or event queues, I had a tremendous difficulty debugging it and also ran into some obscure race conditions and deadlocks that I wasn't smart enough to fix in a way that made me feel confident about the solution (some people might be able to do this but I'm not smart enough). My first iteration design just called a slot directly through a signal and some slots would then spawn other threads to do async work, and that was the hardest to reason about and I was tripping over race conditions and deadlocks. Second iteration used an event queue and that was a little easier to reason about, but not easy enough for my brain to do it without still running into the obscure deadlock and race conditiion. Third and final iteration used the approach described above, and finally that allowed me to create a multithreaded GUI framework that even a dumb simpleton like me could implement correctly.

Then this type of final multithreaded GUI design allowed me to come up with something else that was so much easier to reason about and avoid those types of mistakes I tended to make, and one of the reasons I found it so much easier to reason about at least is because of these homogeneous loops and how they kinda resembled the control flow similar to when I was polling in the DOS days (even though it's not really polling and only performing work when there's work to be done). The idea was to move as far away from the event handling model as possible which implies non-homogeneous loops, non-homogeneous side effects, non-homogeneous control flows, and to work more and more towards homogeneous loops operating uniformly on homogeneous data and isolating and unifying side effects in ways that made it easier to just focus on "what" was happening, not "when" and "where", to reason about the correctness of the code.

6
  • 1
    "like using condition variables to notify threads to wake up" Sounds like an event-based/observer arrangement, not polling.
    – jscs
    Dec 19, 2017 at 14:17
  • I find the difference very subtle but the notification is just in the form of, "There's work to be done" to wake up threads. As an example, an observer pattern might, on resizing a parent control, cascade resize calls calls down the hierarchy using dynamic dispatch. Things would have their resize event functions indirectly called immediately. Then they might repaint themselves immediately. Then if we use an event queue, resizing a parent control might push resize events down the hierarchy, at which point the resize functions for each control might get called in a deferred fashion, at which...
    – user204677
    Dec 19, 2017 at 22:57
  • ... point they might then push repaint events which, likewise, get called in a deferred fashion after everything is finished resizing, and all from a central event handling thread. And I find that centralization beneficial a little bit at least as far as debugging and being able to easily reason about where the processing is taking place (including what thread)... Then what I'm considering to be closest to polling is neither of these solutions...
    – user204677
    Dec 19, 2017 at 22:59
  • It would be, for example, have a LayoutSystem which is normally sleeping, but when the user resizes a control, it would use a condition variable to wake up the LayoutSystem. Then the LayoutSystem resizes all the controls necessary and goes back to sleep. In the process, the rectangular regions the widgets reside in are marked as needing updates, at which point a PaintSystem wakes up and goes through those rectangular regions, repainting the ones needing to be redrawn in a flat sequential loop.
    – user204677
    Dec 19, 2017 at 23:01
  • So the condition variable itself does follow an observer pattern to notify threads to wake up, but we're not transmitting any information more than, "there's work to be done". And each system that wakes up is dedicated to processing things in a very simple loop applying a very homogeneous task, as opposed to an event queue which has non-homogeneous tasks (it could contain an eclectic mixture of events to process).
    – user204677
    Dec 19, 2017 at 23:03
-4

I am giving you a overview more on conceptual way of thinking about the observer patter. Think about a scenario like subscribing to a youtube channels. There are numbers of users who are subscribe to the channel and once there will be any update on the channel which comprises of many video the subscriber get notified that there is a change in this particular channel. So we concluded that if channel is SUBJECT who has the ability to subscribe, unsubscribe and notify all the OBSERVER who are registered in the channel.

1
  • 2
    this doesn't even attempt to address the question asked, when would polling for events be better than using observer pattern. See How to Answer
    – gnat
    Feb 10, 2017 at 10:32

Not the answer you're looking for? Browse other questions tagged or ask your own question.