Home > Articles > Programming

  • Print
  • + Share This
From the author of Release When Possible

Release When Possible

Often, you don't need to keep a lock while you're doing some work. For example, consider a typical producer/consumer system. The producer inserts some work into a queue, and the consumer takes it off. If you protect the queue with a lock, both parts can run entirely independently except for a brief period.

One example of such a system is a media playback framework. When you read a media stream, you first need to demultiplex it. Then you get a set of audio and video packets. You pass off these packets to decoders, which produce uncompressed frames or samples, which in turn get passed off to the display or to speakers.

Each of these steps is independent. The audio and video decoders can each be decoding a sample while the demultiplexer and the playback components run. All of these processes can run with no locks held. They only need to grab a lock when they interact with the queue.

The important thing about this model is that the separate threads (or co-routines multiplexed among a smaller number of threads) don't need to hold locks most of the time, because they're not touching any global state. In fact, they're behaving a lot more like processes than like threads. In this model, you're using threads for the efficiency and convenience of having a shared address space, but most of the time you're actually working in completely independent processes.

This design makes it much easier to reason about the interactions between bits of your program, because you have completely isolated components, which just use locking for communication, as a lighter-weight alternative to passing data via pipes or similar.

  • + Share This
  • 🔖 Save To Your Account