If you are looking for Multi-Threaded application, you must have already tried our very old lock
statement (synclock
in VB.NET). The lock
statement is the most common practice to make certain block of code Thread safe or in other words, it blocks all other Thread from entering the block which one thread is actually executing. The lock statement is actually producing Monitor.Enter in try and Monitor.Exit in finally. Hence it exclusively locks the block for you.
Limitation of Monitor
Now what is the limitation of lock statement? The first is that it locks the whole object and does not give you a chance to provide a Timeout seed. Hence it might produce deadlock situation in certain scenarios, but you can easily handle this using Monitor.TryEnter instead of Enter which lets you specify a timeout.
But the second and most important limitation of Monitor
is that it always provide you an exclusive locking. Thus if a certain resource is read and written by multiple Threads at a time exclusive locking might produce inefficiencies. It is similar to what we see in Databases. Reads are generally cannot be inconsistent when multiple threads are reading it simultaneously only if some thread updates it. Hence exclusive locking is inefficient in case of reading a resource.
ReaderWriterLock
allows shared locks together with exclusive locks. Hence it is possible to read the same resource using sharedlocking, each time the Readerlock is invoked the Lock count is increased but allows you to read the block of code inside it. ReaderWriterLock
also give you an option for exclusive locking using WriteLock statement to eliminate inconsistent reads. Lets see how to use this with code.
static ReaderWriterLock locker = new ReaderWriterLock();
public static List Repository = new List();
public static long Sum()
{
locker.AcquireReaderLock(1000);
long counter = 0;
try
{
for (int i = 0; i < Repository.Count; i++)
Interlocked.Add(ref counter, Repository[i]);
}
finally
{
locker.ReleaseReaderLock();
}
return counter;
}
public static void AddToRepository(int nos)
{
if (locker.IsWriterLockHeld)
return;
locker.AcquireWriterLock(1000);
try
{
for (int i = 0; i < nos; i++)
Repository.Add(i);
}
catch { }
locker.ReleaseWriterLock();
}[/code]
The AddToRepository
is only used to add some integer values to a List. You can easily lock the Repository using lock(Repository)
statement, and that will mean an exclusive locking will be established. On the other hand, the code written inside AddToRepository
also produces exclusive locking using AquireWriterLock
statement. You can optionally specify the timeout value for the WriterLock. (specify TimeSman.Infinite
when you don’t need timeout).
But on the method Sum
which actually reads from the list an exclusive locking with create inefficiencies. We have used AquireReaderLock
in this case to create Shared locking for the block. Hence concurrent reads can took place only when no WriterLock is established, but concurrent writer locks are not possible.
The ReaderWriterLock also have method to escalate from Reader to Writer and vice versa using UpgradeToWriterLock
or DowngradeFromWriterLock
Every lock should always specify its Release statement in finally.
I hope this would come handy.
Thanks for reading.
Pingback: Use of SpinLock for ThreadLocking - Daily .Net Tips
this is nice information need to know more
I am glad you like my post. Thanks
Use ReaderWriterLockSlim for better performance
Useful information. Very helpful, I look forward to reading more of your posts.
Pingback: Cheatsheet: 2011 08.08 ~ 08.16 - gOODiDEA.NET