Master Managed Threading and Synchronization Techniques (cont'd)
Read-Writer Locks
Read-Writer locks help in optimizing synchronization where you have more threads executing read operations than write operations. Read-Writer locks allow multiple read-only threads to enter the code, but allow only one writer thread at a time. You use the ReadWriterLockSlim class to implement Read-Writer locks.
advertisement


Consider the code sample below, in which three threads try to acquire a read lock while one thread tries to acquire a writer lock. The writer thread is able to acquire the lock only after all the reader threads have released their locks. Similarly, a reader thread can acquire the lock only after the writer thread releases it. However, multiple reader threads can acquire read locks at the same time:

   // Read-Writer Lock example
   namespace ThreadingInDotNet
   {
      class Program
      {
         static void Main(string[] args)
         {
            ThreadSafeType tst = new ThreadSafeType();
            Thread t1 = new Thread(new ThreadStart(tst.GetValue));
            Thread t2 = new Thread(new ThreadStart(tst.GetValue));
            Thread t3 = new Thread(new ThreadStart(tst.Increment));
            Thread t4 = new Thread(new ThreadStart(tst.GetValue));
            t1.Start();
            t2.Start();
            t3.Start();
            Thread.Sleep(500);
            t4.Start();
         }
      }
   
      class ThreadSafeType
      {
         ReaderWriterLockSlim counterLock = new ReaderWriterLockSlim();
         int counter = 0;
         public void Increment()
         {
            counterLock.EnterWriteLock();
            Console.WriteLine(" In Writer Lock, Thread ID : " + 
               Thread.CurrentThread.GetHashCode());
            counter++;
            counterLock.ExitWriteLock();
            Console.WriteLine(" Exited Writer Lock, Thread ID : " + 
               Thread.CurrentThread.GetHashCode());
         }
   
         public void GetValue()
         {
   
            counterLock.EnterReadLock();
            Console.WriteLine("In Reader Lock, Thread ID : " + 
               Thread.CurrentThread.GetHashCode());
            Thread.Sleep(3000);
            Console.WriteLine("Thread ID : "+ 
               Thread.CurrentThread.GetHashCode() + 
               " Counter Value : " + counter);
            counterLock.ExitReadLock();
            Console.WriteLine("Exited Reader Lock, Thread ID : " + 
               Thread.CurrentThread.GetHashCode());
         }
      }
   }
Running the preceding code produces the following output:

   In Reader Lock, Thread ID : 3
   In Reader Lock, Thread ID : 4
   Thread ID : 3 Counter Value : 0
   Exited Reader Lock, Thread ID : 3
   Thread ID : 4 Counter Value : 0
   Exited Reader Lock, Thread ID : 4
    In Writer Lock, Thread ID : 5
    Exited Writer Lock, Thread ID : 5
   In Reader Lock, Thread ID : 6
   Thread ID : 6 Counter Value : 1
   Exited Reader Lock, Thread ID : 6
Semaphores
Semaphores control access to a resource or pool of resources by restricting the number of clients that an access the resource at any given moment. They can be either local or named system-wide. You use the System.Threading.Semaphore class to implement semaphores.

Semaphores do not enforce thread affinity. Threads enter the semaphore by calling the WaitOne method. The semaphore count is decremented each time a thread enters and incremented when the thread exits.

In the code sample below, five threads try to enter the code region, but the code restricts the maximum access count to three during the semaphore's construction:

   // local Semaphore example
   class Program
   {
      static Semaphore sph;
      static void Main(string[] args)
      {
         sph = new Semaphore(3, 3);
         Thread th;
         for (int i = 0; i < 5; i++)
         {
            th = new Thread(new ThreadStart(PrintThreadId));
            th.Start();
            Thread.Sleep(1000);
         }
      }
      public static void PrintThreadId()
      {
         sph.WaitOne();
         Console.WriteLine("Thread ID:{0} enters semaphore," 
            Thread.CurrentThread.ManagedThreadId);
         Thread.Sleep(3000);
         Console.WriteLine("Thread ID:{0} exiting semaphore," 
            Thread.CurrentThread.ManagedThreadId);
         sph.Release();
      }
   }
Below is the output of the above code, which shows that the fourth thread (Thread ID 6) could enter the PrintThreadId method only after one of the first three threads exits the semaphore:

   Thread ID:3 enters semaphore
   Thread ID:4 enters semaphore
   Thread ID:5 enters semaphore
   Thread ID:3 exiting semaphore
   Thread ID:6 enters semaphore
   Thread ID:4 exiting semaphore
   Thread ID:7 enters semaphore
   Thread ID:5 exiting semaphore
   Thread ID:6 exiting semaphore
   Thread ID:7 exiting semaphore
Previous Page: Interlocked Operations Next Page: Signaling using EventWaitHandle


Page 1: IntroductionPage 5: Interlocked Operations
Page 2: Thread ManagementPage 6: Read-Writer Locks and Semaphores
Page 3: Synchronization TechniquesPage 7: Signaling using EventWaitHandle
Page 4: Synchronization with Mutexes