Credit where credit is due - some of the ideas here are also embodied in
Jeffrey Richter's
Safe Thread Synchronization
article, and others are in Ian Griffiths'
blog entry on locking with timeouts.
The C# team have also said that were they designing C# again now, they wouldn't have included the lock
keyword, instead making sure that a mechanism for using the using
statement for the same
job would be available. (My main problem with the lock
keyword is that it's the name I would
almost always naturally use for the variable containing the monitor in a single-lock class.)
Both Java and .NET made the same mistake when it came to locking, I believe. At first it seems like having a monitor for every object is a good idea, but in practice if you care about monitor privacy, you need to have an extra field in classes which need to worry about thread safety anyway. This is basically the point Jeffrey Richter makes in the article referenced above, and the same thought had been going through my head before I saw his elegant explanation of it.
I then also heard about Ian Griffiths' quest for attempts to acquire monitors with a timeout,
in a way which didn't make code really hard to follow. Using Monitor.TryEnter
will work, but then you've got to put the finally
block in yourself, and test
the return value from the method. Ian reasoned that usually what you really want is just an
exception if you can't acquire a lock within a reasonable time limit - it indicates that
you've got deadlock, basically.
What intrigued me more than the idea was the implementation - specifically, using the using
statement to neatly acquire the lock and release it at the end of a block. This page (and the referenced code)
attempts to combine the two ideas, to give an alternative to normal locking which is clearer in intention
than using a plain object, which still maintains the neatness of code, and which allows locking with timeouts.
Over time, I've expanded the initial concept to allow deadlock detection in terms of locks having an order imposed on them, and made a few other tweaks. The code is available as part of my Miscellaneous Utility library, and the usage page has more details of exactly what's available. This page now just covers the basic principles.
Essentially, the three primary types involved are:
SyncLock
LockTimeoutException
TimeoutException
, but that seems to be specific to Windows Services, annoyingly enough
- to my mind, it should have been in the System
namespace.)
LockToken
SyncLock
when the monitor has been successfully locked
- 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.
The typical usage is very straightforward:
// Create the lock - almost always as a field // This constructor overload creates a named // lock with a 20 second default timeout. Either the // name or the default timeout can be omitted. SyncLock syncLock = new SyncLock ("Some name"); // Lock it using its default timeout using (syncLock.Lock()) { // Do stuff here } // ... or lock it with a specific timeout using (syncLock.Lock(10000)) { } |
Please mail me with any comments or criticisms.
Back to the main C# page.