Guidelines of when to use locking

I would like to know if there are any guidelineswhich a developer should follow as to when (and where) to place locks.

For instance: I understand that code such as this should be locked, to avoid the possibility of another thread changing the value of SomeHeapValue unexpectedly.

class Foo
{
  public SomeHeapObject myObject;
  public void DoSummat(object inputValue_)
  {
    myObject.SomeHeapValue = inputValue_;
  }

}

My question is, however, how deep does one go with the locking? For instance, if we have this code:

class Foo
{
  public SomeHeapObject myObject;
  public void DoSummat(object inputValue_)
  {
    myObject.SomeHeapValue = GetSomeHeapValue();
  }

}

Should we lock in the DoSummat(...) method, or should we lock in the GetSomeHeapValue() method?

Are there any guidelines that you all keep in mind when strcturing multi-threaded code?


The best guide for locking and threading I found, is this page (this is the text I consult when working with locking and threading):

http://www.albahari.com/threading/

You want the paragraph "Locking and Thread Safety", but read the rest as well, it is very well written.


  • Lock as little as possible, but as much as needed.

  • Avoid locks when possible - in .NET 4.0 there are alternatives that are not causing a context switch.

  • Try not to lock multiple times. Structure your API accordingly. For example a queue. DeQueue - make an alternative DeQueue(int amount) that can dequeue many items with one lock.


Here are some guidelines, aimed for developers relatively novice in multithreading:

  1. Identify the mutable¹ shared² state of your program. Which mutable variables, objects, properties and fields can be accessed by more than one threads during the lifetime of the application, either concurrently or sequentially?

  2. Protect all access to the mutable shared state using a lock. Both write and read operations should be protected. A single unprotected entry point to the mutable shared state of your program is enough to invalidate your locking scheme, and make the behavior of your program undefined.

  3. Use a single locker object to protect all of the mutable shared state. Use multiple lockers only if each locker protects completely isolated islands of mutable shared state.

  4. Release the lock as soon as possible. Make sure that the protected regions include operations only on state that is shared and mutable. Don't do anything unrelated to this state inside these regions.

If you follow these guidelines you'll be able to reason about the correctness of your program, and your program will perform reasonably well.

All of these guidelines are bendable, and experts in multithreading can get away by not following them in all occasions, for performance, convenience or other reasons. But the experience required before being able to judge correctly whether it's safe to bend any of these guidelines is huge. So be careful not to become overconfident with yourself too early!

¹ Changeable, not read-only.
² Shared by multiple threads.