Zooko got me thinking about multi-threaded vs. event-based programming again. Damn you, Zooko! ;-) Here’s my contribution to the ongoing dialog.

One of my favorite observations is that all programmers get to eat worms – sometimes you just get to choose which can to open. This is a perfect example of that phenomenon. I’m going to offer some “compare and contrast” comments, but first I want to point out that both are full of dangers for the poorly prepared – deadlock, livelock, race conditions, timing/ordering problems, you name it. Also, I need to point out that there are many flavors of event-based programming. Finite state machines allow you to use explicit state tables and a greater number of event handlers to replace a smaller number of event handlers full of conditionals. The “promises” used in E let you create arbitrarily complex webs of actions which will occur when other actions complete, with certain safety guarantees. It’s even possible to have a program that’s both multithreaded and event-based, if you allow parallel event-handler dispatch. All flavors of event-based programming tend to have a lot in common, though, so my comments are pretty “flavor-agnostic” except that I tend to assume that events are being used as an alternative to multithreading and thus I tend to assume that event-based programs are not also multithreaded.

  • Event-based programs tend to perform better on uniprocessors, and when event handlers can all complete quickly. Spawning new threads is notoriously expensive, and context switches don’t come for free either. Point: EB.
  • Multithreaded programs can perform better on multiprocessors, when event handlers might need to block for extended periods, or when the code makes external calls where no asynchronous interfaces are available. In this last case, adaptations to event-based programming are possible but almost always exact a heavy toll on performance. Point: MT.
  • With multithreading, you always seem to get an endless proliferation of locks that you’re constantly taking and releasing, always having to worry about lock hierarchies and deadlock. Point: EB.
  • With event-based programming you always seem to get an endless proliferation of events and/or states and their associated functions. Point: MT.
  • Both deadlock and livelock are problems with both approaches, usually taking the form of dead-end states or event starvation when using the event-based model. E’s promises offer some hope for the future in this regard. Half point: EB.
  • Event-based programming requires that all state be explicit, and (largely for this reason) loops are not handled well. Common idioms such as retry loops or scanning multiple objects can get to be very painful. Point: MT.
  • Event-based programs can be more conservative of stack space because everything runs directly from the dispatcher. On the other hand, the absence of stack traces can make debugging more difficult. On the third hand, event logs can take the place of stack traces in most cases. Half point: EB.

As you can see, it’s pretty much a draw. That’s really the point: there’s room for both. Neither is “wrong”; use the right tool for the job at hand. Often one concern or another will leave you with no choice. Idealism and purity are for scientists and dreamers; engineers who have jobs to do should consider all alternatives and all factors that allow them to choose one alternative over another.