Namespace: MiscUtil.Threading.
      Types involved: SyncLock, OrderedLock,
      LockToken, LockOrderException, LockTimeoutException
    
These classes have evolved from ideas put forward by Jeffrey Richter and Ian Griffiths, and written up on one of my threading pages. My hope is that they'll continue to evolve, but there is a performance issue - extra features tend to chip away at performance, and while I don't tend to worry too much about performance, it's clearly an issue when it comes to something as basic as locking. (This is especially true as if people are worried about the cost of locking, they'll tend to try to do clever things to avoid it, which is always a bad idea.)
      Instead of using the lock statement on an object reference, you need to create an
      instance of one of the lock classes (currently SyncLock and OrderedLock),
      and then call one of the Lock method overloads to acquire the lock. The Lock
      methods each return a LockToken. Calling Dispose on the returned 
      LockToken releases the lock. The locks are re-entrant, just as with the normal lock
      statement, so if you acquire a lock twice and release it once, you still own the lock and need to release
      it again in order to fully release ownership.      
    
      Now, manually calling Dispose will work, but is error-prone. A much better idiom is to use
      the using statement in C# (or the Using statement in VB 8 or higher). Here's an example:
    
using MiscUtil.Threading; class Example { SyncLock padlock = new SyncLock(); void Method1 { using (padlock.Lock()) { // Now own the padlock } } void Method2 { using (padlock.Lock()) { // Now own the padlock } } }  | 
      Now, the above is using SyncLock to give exactly the same semantics as the lock
      statement. The only advantage is that because the padlock variable is of type SyncLock,
      it's very obvious that it's there for locking and not for anything else.
    
      The LockToken returned by the Lock method is a struct.
      It must be disposed in order to release the monitor. The reason for making this a struct
      rather than a class is that it then only takes up a bit of stack space for each lock operation,
      rather than needing to create a whole extra object each time, which would be nasty for performance.      
    
      Note that LockToken doesn't have any finalizer (or anything faking that). If you forget
      to call Dispose on the lock token, you're in trouble. You'll either get a
      deadlock or a timeout exception sooner or later. I'd argue this is actually better than
      getting a less deterministic deadlock due to the GC running a finalizer. (Possibly
      IDisposable should have been a "hard" contract requiring disposal
      in the first place; this pattern is violating the lax nature of IDisposable,
      but at least it's for good reasons.)      
    
      LockToken doesn't have anything else one can do with it other than call Dispose,
      so assuming the using statement is available to you, there should be very few situations where
      you actually need to declare your own variable to hold it - just let the compiler create an anonymous
      one for you as in the code above.
    
Monitor property
      You may wish to use methods from System.Threading.Monitor such as Wait
      and Pulse. Rather than copying all the methods from that class into SyncLock,
      a lock exposes the monitor that it internally acquires with the Monitor property. This should
      be used carefully, however - if you call Monitor.Enter or Monitor.Exit and pass
      in the monitor used by a lock, you could stop the lock from appearing to work properly. Use with care. Here's a 
      sample snippet:
    
// ... or if you want to be able to wait/pulse the monitor using (LockToken token = syncLock.Lock(10000)) { Monitor.Wait (syncLock.Monitor); // or Monitor.Pulse (syncLock.Monitor); }  | 
      Design note: It's tempting to remove this property and just provide the Pulse, PulseAll
      and Wait methods instead. However, as more functionality may be included in future versions of
      the Monitor class itself, it would be tricky to keep SyncLock in step. The advantage,
      of course, would be that messing things up would be harder - the class itself would be more robust. If enough
      people think the property should be removed, I'm willing to reconsider; the decision is on a bit of a knife-edge
      as it is. Please mail me with your thoughts on this. (The change would
      be backwardly incompatible, of course - but very easy to fix up in client source code. The library itself currently isn't
      versioned, and nor is it likely to be in the near future. People are likely to take what they want either by using
      (and perhaps modifying) an existing version, or just including the relevant parts of the source into their own
      source tree.)
    
      This is probably the most straightforward feature of SyncLock - locks can have names. They're read-only,
      and are specified in the various constructor overloads available. They can be handy when debugging, and appear in the
      messages of timeout exceptions when they're thrown.
    
      Arguably, simplifying attempting to acquire a lock with a timeout was the principal reason for Jeffrey and Ian's
      work in the first place. Simply put, each lock attempt has a timeout associated with it. If the timeout period expires
      without the lock being acquired, a LockTimeoutException is thrown. Note that this doesn't necessarily mean
      there is a bug in your code - just occasionally, things holding locks will take a long time, even though it's
      wise to try to hold locks for as short a period as possible.
    
      The timeout can be specified as a number of milliseconds, or a TimeSpan value. The value 
      Timeout.Infinite may be used to specify that the thread should wait as long as it takes (possibly forever)
      to acquire the lock. (This is the normal behaviour of the lock statement.)
    
      If no timeout is specified, the default timeout for the lock is used. Each lock may have its own default timeout specified
      in its constructor (as a number of milliseconds), and if no default timeout is specified, the value of 
      the static SyncLock.DefaultDefaultTimeout property is taken as the default timeout at construction time.
      (Changes to DefaultDefaultTimeout after a lock has been created don't affect the default timeout for that lock.)
      The initial value of DefaultDefaultTimeout is Timeout.Infinite. This means that unless
      any timeouts are specified, the locks will behave much like "normal" .NET locks.
    
      Here's an example showing the three overloads of Lock():
    
using MiscUtil.Threading; class Example { // Set the default timeout to 10 seconds SyncLock padlock = new SyncLock(10000); void Method1 { // Use the default timeout using (padlock.Lock()) { } } void Method2 { // Use a timeout of 30 seconds using (padlock.Lock(30000)) { } } void Method3() { // Use a timeout of 1 minute using (padlock.Lock(new TimeSpan(0,1,0))) { } } }  | 
      As well as SyncLock, an OrderedLock class is provided. This allows
      the concept of one lock having another OrderedLock as an "inner lock". The rule used
      to avoid deadlock is simple: you can't acquire a lock when you already own the inner lock of
      that lock. Each lock only has one direct inner lock, but the "innerness" is transitive: if you
      have three locks, outer, middle and inner, 
      with the inner lock of outer being middle and
      the inner lock of middle being inner, then inner is
      also considered an inner lock of outer. You can acquire an inner lock without
      first acquiring the outer lock (otherwise there'd be no point in having two locks) but you
      can't acquire the inner lock and then the outer lock. You can, however, acquire the outer lock,
      then the inner lock, then the outer lock again. This can't cause deadlock, so is allowed. Here
      are some sample sequences of ordering, using the three locks described above.
      (This is assuming that no locks are released, by the way - you can obviously acquire the inner lock, 
      release it, and then acquire the outer lock.)
    
      The inner lock can be set either using the InnerLock property, or using the
      SetInnerLock() method. The use of the latter is that it returns the lock you
      call it on (the outer lock, not the inner one). This allows code such as:
    
using MiscUtil.Threading; class Example { static OrderedLock inner = new OrderedLock("Inner"); static OrderedLock outer = new OrderedLock("Outer").SetInnerLock(inner); }  | 
Note that this idiom only applies (in C# at least) to static field initializers; instance field initializers cannot reference the instance being created, unfortunately. Setting inner locks for instance variables should be done in the constructor, like this:
using MiscUtil.Threading; class Example { OrderedLock inner = new OrderedLock("Inner"); OrderedLock outer = new OrderedLock("Outer"); Example() { outer.InnerLock = inner; } }  | 
outer middle innermiddle innerouter innerouter middle inner middle outerouter middle outer inner middlemiddleinner middleinner outermiddle outerouter inner middle
      OrderedLock verifies the lock ordering before it tries to call Monitor.TryEnter.
      If the acquisition of the lock violates the ordering rules, a LockOrderException is thrown.
      This exception always indicates a bug in your code, as you have violated the rules you've set yourself
      by describing the relationships between locks.
    
      OrderedLock exposes another property, Owner, which indicates the thread which 
      currently owns the lock, or null if the lock is not owned by anyone. Keeping track of this
      information is required in order to verify lock ordering, but incurs a performance penalty (see the 
      performance section below for details), which is why it is not available 
    
      OrderedLock does not ensure that locks are released in the order they are
      acquired. This would give a significant performance penalty without being useful for most of
      the time. When locks are used with the using statement (which is the expected use)
      it is impossible for the release order to end up as anything other than the reverse of the acquisition order.
    
Performance of locking is important. It's important because the more expensive it is, the more people will try to avoid it, attempting to be clever and usually writing code which is not thread-safe in the process. The Miscellaneous Utility locking types are not quite as "pure" as they might be if performance were not an issue - there are places where code is inlined rather than calling a separate method, for instance, purely for (tested) performance reasons.
      SyncLock and OrderedLock are both reference types with a fairly small
      footprint (partly depending on the length of the name chosen). OrderedLock takes up slightly
      more memory than SyncLock, but unless you are likely to have an awful lot of long-lived
      locks, it's unlikely to be a significant problem.
    
      LockToken is a value type which just contains a reference to its "parent" lock. The reason
      for it being a value type is so that it can be allocated on the stack in the typical use case, thereby
      removing any heap allocation and garbage collection penalty.
    
      Speed of code like this is deeply dependent on the exact configuration of the computer used. So far,
      I only have results for the laptop I've been using to develop the code and my desktop at work.
      The laptop has a single Pentium-M single-core processor and fairly fast memory. The desktop is a 
      P4; memory type unknown. TODO - provide more detailed specs. The tests are currently run against .NET 1.1. If you 
      are able to provide more results, I'd be happy to include them on this page. Please run the performance 
      unit tests from the source distribution, making sure you build the Release configuration, and 
      mail me the output from the console. If you're using NUnit GUI, 
      look at the Console.Out tab.
      (The Debug configuration is significantly slower,  but I don't regard that as an issue, as the 
      Release-built library should generally be used anyway.) The performance measurements below are 
      factors compared with "native" locking. For example, a factor of 2 would mean that using the locking 
      provided by the library took twice as long as using "native" locking (the lock statement).
    
| Description | Factor | ||
|---|---|---|---|
| Laptop | Desktop | x64 | |
          Acquiring and releasing a previously unowned SyncLock
         | 
        1.82 | 2.26 | 1.04 | 
          Acquiring and releasing a SyncLock which was previously
          owned by the current thread
         | 
        1.66 | 2.12 | 1.06 | 
          Acquiring and releasing a previously unowned OrderedLock
          which has no inner locks
         | 
        3.46 | 3.49 | 2.08 | 
          Acquiring and releasing an OrderedLock which was previously
          owned by the current thread and which has no inner locks
         | 
        3.07 | 3.11 | 1.84 | 
          Acquiring and releasing a previously unowned OrderedLock
          which has two inner locks
         | 
        3.68 | 3.93 | 2.20 | 
          Acquiring and releasing an OrderedLock which was previously
          owned by the current thread and which has two inner locks
         | 
        3.31 | 3.32 | 1.96 | 
      As you can see, the performance is still reasonable, especially for SyncLock.
      Very few applications are likely to see a significant performance degredation due to using
      the locks, and I believe the benefits easily outweigh the slight performance loss. As an
      example of how fast acquiring locks is, the first test managed to acquire a billion
      locks in under 45 seconds on my laptop. That's over 22 million locks per second. Unless you're
      acquiring at least a couple of hundred thousand locks per second, the performance difference
      made by using these locks is going to be lost in the noise.
    
Back to the main MiscUtil page.