Home > DeveloperSection > Articles > Synchronization in C#

Synchronization in C#


C# C#  .NET  Thread  Threading  Synchronization 
Ratings:
1 Comment(s)
 1321  View(s)
Rate this:

Introduction:


In a multithreaded environment, each thread has its own local thread stack and registers. If multiple threads access the same resource for read and write, this will cause a resource conflict and the value may be changed unexpectedly and produce inconsistent result. For example, let's say our application contains two threads, one thread is for reading content from the file and another thread is writing content to the file. If the write thread tries to write and the read thread tries to read the same data in a particular instance, the data might become corrupted. In this situation, we need to lock the file access. The thread synchronization has two stages. Signaled and non-signaled.

The signaled state allows objects to access and modify data. The non-signaled state allow accessing or modifying the data in the thread local stack.

Blocking:

If thread execution is paused for some reason, such as when it is in sleeping or waiting state. A blocked thread immediately yields its processor time slice, and consumes no processor time until its blocking condition is satisfied. You can test for a thread being blocked via its ThreadState property:

bool blockingStatus = (thread1.ThreadState & ThreadState.WaitSleepJoin)! =0;

 

When a thread blocks or unblocks, the operating system performs a context switching. Unblocking may happen in one of four ways:

1.  When Blocking condition has been satisfied

2.  Operation is timed out in a specified time span.

3.  by causing interrupts via Thread.Interrupt;

4.  by aborting via Thread.Abort;


Locking:

Exclusive locking is used to ensure that only one thread can enter particular sections of code at a time. The two main exclusive locking constructs are lock and Mutex These two locks are faster and more convenient. 

For example,

using System;
using System.Threading;
namespace QueueWorkerEx
{
    class Program
    {
        static bool b;
        static void Main()
        {
            new Thread(demoMethod).Start();
            demoMethod();
            Console.ReadKey();
        }
        static void demoMethod()
        {
            if (!b)
            {
                Console.WriteLine("demo method");
                b = true;
            }
        }
    }
}

 

Output:

demo method

demo method

 

Monitor:

C# lock statement is a call to the methods Monitor.Enter and Monitor.Exit, with a try/finally block. It can be done by acquiring a significant lock so that only one thread can enter in a given piece of code at one time. Monitor is no different from lock but the monitor class provides more control over the synchronization of various threads trying to access the same lock of code. Using a monitor, it can be ensured that no other thread is allowed to access a section of application code being executed by the lock owner, unless the other thread is executing the code using a different locked object.

The Monitor class has the following methods for the synchronize access to taking and releasing a lock:

  1. ·         Monitor.Enter
  2. ·         Monitor.TryEnter
  3. ·         Moniter.Exit

 

Monitor locks objects (i.e., reference types), not value types. While you can pass a value type to Enter and Exit, it is separated for each call. Wait releases the lock if it is held and waits to be notified. When Wait is notified, it returns and obtains the lock again. Pulse and PulseAll signal are waiting queue for the next thread to proceed.

The following is the syntax for using a monitor.

 

using System;
using System.Threading.Tasks;
using System.Threading;
namespace TPLEx
{
    class MoniterEx
    {
        static void Main()
        {
 
            try
            {
                int x = 1;
 
                Monitor.Enter(x);
                try
                {
                    // Code that needs to be protected by the monitor.  
                }
                finally
                {
 
                    Monitor.Exit(x);
                }
            }
            catch (SynchronizationLockException SyncEx)
            {
            Console.WriteLine("A SynchronizationLockException occurred in the program.");
                Console.WriteLine("Exception is: "+SyncEx.Message);
            }
            Console.ReadKey();
        }
 
    }
}

Output:


When to lock: If there is any writable shared field used in your code then you must write code inside a lock for ensuring that another thread does not enter a critical section of code while a thread is already executing in the critical section. If another thread tries to enter a locked code, it will wait, block, until the object is released.


 Nested Locking:

 A thread can repeatedly lock the same object in a nested fashion:


lock (locker)
  lock (locker)
    lock (locker)
    {
       // Do something
    }
 
For example:
static readonly object _lock = new object();
 
static void Main()
{
  lock (_lock)
  {
     demoMethod(); // We still have the lock - because locks are nested.
  }
}
 
static void demoMethod()
{
  lock (_lock) { Console.WriteLine ("In Demo Method"); }
}

 

Deadlock: 

A deadlock happens when two threads each wait for a resource held by the other, so neither can proceed. For example, if one thread has acquired a lock but at the mean time another thread is waiting for the same resource but another thread is already acquire a lock which is needed by first thread. So, in this situation, nor the first thread release a lock neither the second thread acquire that lock. So, deadlock situation occurred. For example:

object lock1 = new object();
object lock2 = new object();
 
new Thread (() => {
                    lock (lock1)
                    {
                      Thread.Sleep (2000);
                      lock (lock2);      // Deadlock situation
                    }
                  }).Start();
lock (locker2)
{
  Thread.Sleep (2000);
  lock (lock1);                          // Deadlock situation
}
 

 

 

Mutex:

A Mutex is like a C# lock, but it can work across multiple processes. In other words, Mutex can be computer-wide as well as application-wide. Acquiring and releasing a lock in  Mutex takes a few microseconds — about 50 times slower than a lock.

For example:

 

using System;
using System.Threading;
namespace MutexEx1
{
    class MutexEx
    {
        static void Main()
        {
            using (var mutex = new Mutex(false, "Test MutexEx"))
            {
                if (!mutex.WaitOne(TimeSpan.FromSeconds(3), false))
                {
                    Console.WriteLine("Another app instance is running. Bye!");
                    return;
                }
                RunProgram();
            }
        }
        static void RunProgram()
        {
            Console.WriteLine("Running. Press Enter to exit");
            Console.ReadLine();
        }
    }
}
 

Output:

 

Running. Press Enter to exit

 

Semaphore:

Use the Semaphore class to control access to a pool of resources. Threads can enter the semaphore by calling the WaitOne method, which is inherited from the WaitHandle class, and release the semaphore by calling the Release method.

The count on a semaphore is decremented each time a thread enters the semaphore, and incremented when a thread releases the semaphore. When the count is zero, subsequent thread  will requests the block until other threads release the semaphore. When all threads have released the semaphore, the count is at the maximum value specified when the semaphore was created.

There is no guaranteed order, such as FIFO or LIFO, in which blocked threads can enter the semaphore.

A thread can enter the semaphore multiple times, by calling the WaitOne method repeatedly. To release some or all of these entries, the thread can call the parameter less Release () method and overload it multiple times, or it can also call the Release (Int32) method and overload it, were the int value here specifies the number of entries to be released.

For example, we have created a semaphore of capacity three, which executes demoMethod () by the thread.

 

 

using System;
using System.Threading;
namespace SemaphoreEx
{
    class SemaphoreEx1     
    {
        static SemaphoreSlim _Semaphore = new SemaphoreSlim(3);    // Capacity of 3
 
        static void Main()
        {
            for (int i = 1; i <= 10; i++)
            new Thread(demoMethod).Start(i);
        }
 
        static void demoMethod(object id)
        {
            Console.WriteLine(id + " wants to enter");
            _Semaphore.Wait();
            Console.WriteLine(id + " is in!");         
            Thread.Sleep(1000 * (int)id);             
            Console.WriteLine(id + " is leaving Now");    
            _Semaphore.Release();
            Console.ReadKey();
        }
    }
 
}



Event Wait Handles:

Event wait handles are used for signaling. Signaling happens when one thread waits until it receives notification from another. Event wait handles are the simplest form of the signaling constructs.

 They come in three form: AutoResetEvents, ManualResetEvents, and (from Framework 4.0) CountDownEvent. The former two are based on the common EventWaitHandle class, where they derive all their functionality.

For more details, you see also Related Articles:

·         https://www.mindstick.com/Articles/1488/multithreading-in-c-sharp

·         https://www.mindstick.com/Articles/630/threading-in-c-sharp-with-example

·         https://www.mindstick.com/Articles/624/join-sleep-and-abort-methods-in-c-sharp-                       threading

·         https://www.mindstick.com/Articles/11979/multithreading-with-the-backgroundworker-                       component-in-asp-dot- net

·         https://www.mindstick.com/Articles/1496/thread-synchronization-and-set-priority-in-c-                    sharp

·         https://msdn.microsoft.com/en-us/library/kztecsys%28v=vs.110%29.aspx

·         https://msdn.microsoft.com/en-us/library/ms228969%28v=vs.110%29.aspx

·         https://msdn.microsoft.com/en-us/library/dd460693%28v=vs.110%29.aspx

 


Thanks

By Manoj Pandey on   10 months ago
It is really helpful nice article.

Don't want to miss updates? Please click the below button!

Follow MindStick