Threading Best Practices

(Assuming .NET; similar things would apply for other platforms.)

Well, there are lots of things to consider. I'd advise:

  • Immutability is great for multi-threading. Functional programming works well concurrently partly due to the emphasis on immutability.
  • Use locks when you access mutable shared data, both for reads and writes.
  • Don't try to go lock-free unless you really have to. Locks are expensive, but rarely the bottleneck.
  • Monitor.Wait should almost always be part of a condition loop, waiting for a condition to become true and waiting again if it's not.
  • Try to avoid holding locks for longer than you need to.
  • If you ever need to acquire two locks at once, document the ordering thoroughly and make sure you always use the same order.
  • Document the thread-safety of your types. Most types don't need to be thread-safe, they just need to not be thread hostile (i.e. "you can use them from multiple threads, but it's your responsibility to take out locks if you want to share them)
  • Don't access the UI (except in documented thread-safe ways) from a non-UI thread. In Windows Forms, use Control.Invoke/BeginInvoke

That's off the top of my head - I probably think of more if this is useful to you, but I'll stop there in case it's not.


Learning to write multi-threaded programs correctly is extremely difficult and time consuming.

So the first step is: replace the implementation with one that doesn't use multiple threads at all.

Then carefully put threading back in if, and only if, you discover a genuine need for it, when you've figured out some very simple safe ways to do so. A non-threaded implementation that works reliably is far better than a broken threaded implementation.

When you're ready to start, favour designs that use thread-safe queues to transfer work items between threads and take care to ensure that those work items are accessed only by one thread at a time.

Try to avoid just spraying lock blocks around your code in the hope that it will become thread-safe. It doesn't work. Eventually, two code paths will acquire the same locks in a different order, and everything will grind to a halt (once every two weeks, on a customer's server). This is especially likely if you combine threads with firing events, and you hold the lock while you fire the event - the handler may take out another lock, and now you have a pair of locks held in a particular order. What if they're taken out in the opposite order in some other situation?

In short, this is such a big and difficult subject that I think it is potentially misleading to give a few pointers in a short answer and say "Off you go!" - I'm sure that's not the intention of the many learned people giving answers here, but that is the impression many get from summarised advice.

Instead, buy this book.

Here is a very nicely worded summary from this site:

Multithreading also comes with disadvantages. The biggest is that it can lead to vastly more complex programs. Having multiple threads does not in itself create complexity; it's the interaction between the threads that creates complexity. This applies whether or not the interaction is intentional, and can result long development cycles, as well as an ongoing susceptibility to intermittent and non-reproducable bugs. For this reason, it pays to keep such interaction in a multi-threaded design simple – or not use multithreading at all – unless you have a peculiar penchant for re-writing and debugging!

Perfect summary from Stroustrup:

The traditional way of dealing with concurrency by letting a bunch of threads loose in a single address space and then using locks to try to cope with the resulting data races and coordination problems is probably the worst possible in terms of correctness and comprehensibility.


(Like Jon Skeet, much of this assumes .NET)

At the risk of seeming argumentative, comments like these just bother me:

Learning to write multi-threaded programs correctly is extremely difficult and time consuming.

Threads should be avoided when possible...

It is practically impossible to write software that does anything significant without leveraging threads in some capacity. If you are on Windows, open your Task Manager, enable the Thread Count column, and you can probably count on one hand the number of processes that are using a single thread. Yes, one should not simply use threads for the sake of using threads nor should it be done cavalierly, but frankly, I believe these cliches are used too often.

If I had to boil multithreaded programming down for the true novice, I would say this:

  • Before jumping into it, first understand that the the class boundary is not the same as a thread boundary. For example, if a callback method on your class is called by another thread (e.g., the AsyncCallback delegate to the TcpListener.BeginAcceptTcpClient() method), understand that the callback executes on that other thread. So even though the callback occurs on the same object, you still have to synchronize access to the members of the object within the callback method. Threads and classes are orthogonal; it is important to understand this point.
  • Identify what data needs to be shared between threads. Once you have defined the shared data, try to consolidate it into a single class if possible.
  • Limit the places where the shared data can be written and read. If you can get this down to one place for writing and one place for reading, you will be doing yourself a tremendous favor. This is not always possible, but it is a nice goal to shoot for.
  • Obviously make sure you synchronize access to the shared data using the Monitor class or the lock keyword.
  • If possible, use a single object to synchronize your shared data regardless of how many different shared fields there are. This will simplify things. However, it may also overly constrain things too, in which case, you may need a synchronization object for each shared field. And at this point, using immutable classes becomes very handy.
  • If you have one thread that needs to signal another thread(s), I would strongly recommend using the ManualResetEvent class to do this instead of using events/delegates.

To sum up, I would say that threading is not difficult, but it can be tedious. Still, a properly threaded application will be more responsive, and your users will be most appreciative.

EDIT: There is nothing "extremely difficult" about ThreadPool.QueueUserWorkItem(), asynchronous delegates, the various BeginXXX/EndXXX method pairs, etc. in C#. If anything, these techniques make it much easier to accomplish various tasks in a threaded fashion. If you have a GUI application that does any heavy database, socket, or I/O interaction, it is practically impossible to make the front-end responsive to the user without leveraging threads behind the scenes. The techniques I mentioned above make this possible and are a breeze to use. It is important to understand the pitfalls, to be sure. I simply believe we do programmers, especially younger ones, a disservice when we talk about how "extremely difficult" multithreaded programming is or how threads "should be avoided." Comments like these oversimplify the problem and exaggerate the myth when the truth is that threading has never been easier. There are legitimate reasons to use threads, and cliches like this just seem counterproductive to me.


You may be interested in something like CSP, or one of the other theoretical algebras for dealing with concurrency. There are CSP libraries for most languages, but if the language wasn't designed for it, it requires a bit of discipline to use correctly. But ultimately, every kind of concurrency/threading boils down to some fairly simple basics: Avoid shared mutable data, and understand exactly when and why each thread may have to block while waiting for another thread. (In CSP, shared data simply doesn't exist. Each thread (or process in CSP terminology) is only allowed to communicate with others through blocking message-passing channels. Since there is no shared data, race conditions go away. Since message passing is blocking, it becomes easy to reason about synchronization, and literally prove that no deadlocks can occur.)

Another good practice, which is easier to retrofit into existing code is to assign a priority or level to every lock in your system, and make sure that the following rules are followed consistently:

  • While holding a lock at level N, you may only acquire new locks of lower levels
  • Multiple locks at the same level must be acquired at the same time, as a single operation, which always tries to acquire all the requested locks in the same global order (Note that any consistent order will do, but any thread that tries to acquire one or more locks at level N, must do acquire them in the same order as any other thread would do anywhere else in the code.)

Following these rules mean that it is simply impossible for a deadlock to occur. Then you just have to worry about mutable shared data.