A frequent requirement of thread security is to allow concurrent reading but not concurrent writing. For example, this is true for files.
ReaderwriterlockslimIn. NET Framework 3.5It is provided to replace the previous"Fat"Version"Readerwriterlock"
These two classes have two basic locks.----One read lock and one write lock.
A write lock is a full exclusive lock.
Read locks can be compatible with other read locks
Therefore, when a thread holds the write lock, all the threads trying to obtain the read lock and write lock are blocked. However, if no thread holds the write lock, A series of threads can concurrently obtain the read locks.
ReaderwriterlockslimDefines the following methods to get and release Read/write lock.
- Public void enterreadlock ();
- Public void exitreadlock ();
- Public void enterwritelock ();
- Public void exitwritelock ();
AndMonitor. tryenterSimilarly,ReaderwriterlockslimCorresponding"Enterxxx"The corresponding methods are also provided"Try"Version.ReaderwriterlockProvidedAcquirexxxAndReleasexxxMethod. When timeout occurs,ReaderwriterlockThrowApplicationexceptionInstead of returningFalse.
Static Readonly Readerwriterlockslim _ RW = New Readerwriterlockslim ();
Static List < Int > _ Items = New List < Int > ();
Static Random _ Rand = New Random ();
Public Static Void Main ()
{
/// Three read threads
New Thread (read). Start ();
New Thread (read). Start ();
New Thread (read). Start ();
//Two write threads
NewThread (write). Start ("A");
NewThread (write). Start ("B");
}
Static Void Read ()
{
While ( True )
{
_ RW. enterreadlock (); // Get read lock
// Simulate the read Process
Foreach ( Int I In _ Items)
Thread. Sleep ( 100 );
_ RW. exitreadlock (); // Release read locks
}
}
Static Void Write ( Object Threadid)
{
While ( True )
{
Console. writeline (_ RW. currentreadcount + " Concurrent readers " );
IntNewnumber=Getrandomnum (100);
_ RW. enterwritelock (); // Get write lock
_ Items. Add (newnumber ); // Write Data
_ RW. exitwritelock (); // Release write lock
Console. writeline ( " Thread " + Threadid + " Added " + Newnumber );
Thread. Sleep ( 100 );
}
}
// Obtain Random Number
Static Int Getrandomnum ( Int Max ){ Lock (_ Rand) Return _ Rand. Next (max );}
In the actual release version, it is best to use Try/finally To ensure that the lock is correctly released even if an exception is thrown.
ImageCurrentreadcountAttribute,ReaderwriterlockslimThe following attributes are provided to monitor locks.
Updatable lock:
It is useful to upgrade the read lock to a write lock in another atomic operation. For example, if you wantListWhen there are some items that do not exist, You may perform the following steps:
- Obtain a read lock.
- Test. If you want to write something in the list, release the lock and return it.
- Release the read lock.
- Get a write lock
- Add item, write something,
- Release the write lock.
The problem is that another thread may have modified the list between step 3 and step 4.
ReaderwriterlockslimUse an update lock(Upgradeable lock ),To solve this problem.
an updatable lock is similar to a read lock except that it can be converted into a write lock in an atomic operation, you can use it like this:
- call enterupgradeablereadlock to obtain the updatable lock.
- perform some read operations, for example, you can determine that the content to be written is not in the List .
- call enterwritelock, This method will update the locks Upgrade to write lock.
- perform write operations.
- call exitwritelock , this method converts the write lock back to the updatable lock.
- continue to perform some read operations or do nothing.
- call exitupgradeablereadlock release the updatable lock.
From the caller's point of view, it looks like a nested/Recursive lock, in terms of function, in the third step,
ReaderwriterlockslimRelease the read lock in an atomic operation and obtain the write lock.
The important difference between updatable locks and read locks is that:Although the updatable lock can coexist with the read lock, only one updatable lock can be acquired at a time. The main purpose is to prevent deadlocks.
In this way, we can modifyWriteMethod, so that it can add some not in the listItem.
Static Void Write ( Object Threadid)
{
While ( True )
{
Console. writeline (_ RW. currentreadcount + " Concurrent readers " );
IntNewnumber=Getrandomnum (100);
_ RW. enterupgradeablereadlock (); // Obtain updatable locks
If ( ! _ Items. Contains (newnumber )) // If the content to be written is not in the list
{
_ RW. enterwritelock (); // Update locks into write locks
_ Items. Add (newnumber ); // Write
_ RW. exitwritelock (); // Update lock again
Console. writeline ( " Thread " + Threadid + " Added " + Newnumber ); // Read data
}
_ RW. exitupgradeablereadlock (); // Exit updatable lock
Thread. Sleep ( 100 );
}
}
From the above example, we can see that the read/write locks provided by C # are powerful and easy to use,
Therefore, when writing a read/write lock, consider whether you need to support the updatable lock and whether it is necessary to write a read/write lock by yourself.
References:
Http://www.albahari.com/threading/
CLR via C #3.0