(1 item) |
|
(1 item) |
|
(5 items) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(6 items) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(4 items) |
|
(2 items) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(2 items) |
|
(5 items) |
|
(3 items) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(3 items) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(8 items) |
|
(2 items) |
|
(7 items) |
|
(2 items) |
|
(2 items) |
|
(1 item) |
|
(2 items) |
|
(1 item) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(5 items) |
|
(1 item) |
|
(3 items) |
|
(2 items) |
|
(2 items) |
|
(8 items) |
|
(7 items) |
|
(3 items) |
|
(7 items) |
|
(6 items) |
|
(1 item) |
|
(2 items) |
|
(5 items) |
|
(5 items) |
|
(7 items) |
|
(3 items) |
|
(7 items) |
|
(16 items) |
|
(10 items) |
|
(27 items) |
|
(15 items) |
|
(15 items) |
|
(13 items) |
|
(16 items) |
|
(15 items) |
It's clearly a threading/scheduling kind of a day...
Matthew Adams has justly taken me to task by
email over a lack of balance in my blog on
the relative merits of
the ReaderWriterLock
and Monitor
.
When I wrote that, it was mainly as a counterpoint to Sebastien Lambla's
article
advocating the ReaderWriterLock
. But taken in isolation, my article is a little bit too down on the
ReaderWriterLock
. While I believe everything I wrote is technically correct, I neglected to mention that
there are cases where ReaderWriterLock
is A Good Thing. This may have left readers with the
impression that I thought that Monitor
is always a better choice; that's not what I was intending to
say - I merely think that ReaderWriterLock
doesn't always offer the improvements you might be
expecting.
So what would a scenario where the ReaderWriterLock
is a good solution look like?
As I said in the previous article, the right choice of locking primitive is determined both by the pattern of access and
the level of contention. The example I showed is a bad candidate for the ReaderWriterLock
because the
lock protects a very small amount of data, and the code holds the lock for a very short period. In other words, the
example is using a fine-grained locking strategy.
ReaderWriterLock
can be a whole lot more useful is if you are locking at a much more
coarse-grained level. (I.e., using locks to protect large bodies of data, rather than tiny pieces.) Matthew gave the
example of an MVC environment, with several subscribers to a model. Most of the time, when these subscribers
access the model, they are reading from it. In his system, these subscribers often do non-trivial amounts of work while
holding a read lock on the model, with some of the processing involving network IO. Because of this potential for
blocking, the observers do this work on worker threads. There is a tendency for all of the observers to want to read
simultaneously because they are triggered by events raised by the model they all observe. The speed with
which updates are processed is important, because many of the observers are responsible for keeping the screen
up to date. (Obviously using Control.BeginInvoke
where necessary when multiple threads have
been used.) You only have a limited budget of time in which to process things before the UI will start to feel
sluggish.
All of this makes the system likely to benefit from the ReaderWriterLock
. The lock is going to be held
for a relatively long time, and there will be a correspondingly high probability that multiple threads will be looking to
acquire the lock concurrently. Whereas in my non-ReaderWriterLock
-friendly example, the likelihood of a
context switch occurring while the lock was held was very small, in Matthew's scenario, is it almost certain to happen.
This is where the ReaderWriterLock
shines - if most of the time you get read-only
access with the odd update, it can allow the reads to progress concurrently.
This is a real system by the way, rather than a hypothetical scenario, just in case you weren't sure. It originally
used Monitors
, and typically took several seconds to open up the UI on a given bit of the model, as all
the various views and other observers took it in turns to read and process the data they required. By changing it to use
a ReaderWriterLock
, this time was reduced to a fraction of a second.
ReaderWriterLock
is arguably at its best when used like this - enabling a coarse grained locking
strategy rather than a complex, fiddly, fine-grained strategy with many locks, without compromising performance.