C# Thread Synchronization

In C# programming, thread synchronization is a technique frequently employed in multithreaded programming to control the access of multiple threads to shared resources. This practice is essential for maintaining data integrity and preventing race conditions in applications with multiple threads.

In a program utilizing multithreading, threads have the freedom to access any necessary resource during their execution. Threads collaborate on resources and operate independently of each other. Nonetheless, managing access to shared resources, such as data, is a crucial responsibility that can potentially result in unforeseen outcomes. To mitigate these potential issues, thread synchronization methods are frequently employed to regulate the interactions of threads with shared resources.

When concurrent threads access and alter shared data simultaneously, it can introduce potential problems like race conditions. A race condition emerges when multiple threads try to modify a shared variable concurrently, resulting in inaccurate or unforeseen outcomes.

Syntax:

It has the following syntax.

Example

synchronizationObject.Wait();   // Request / Wait for the lock

// Critical section (shared resource code)

synchronizationObject.Release(); // Release the lock

In the given syntax,

  • Wait: It is the method that is used to request access to the shared resources.
  • Critical Section: It is the portion that contains the code that accesses the shared resources, such as variables, lists, files, etc.
  • Release: This method is used to release the lock after the thread completes its work in the critical section.
  • C# Thread Synchronization Example

In this instance, we will utilize an illustrative scenario to showcase the concept of thread synchronization in C#.

Example

Example

using System;

using System.Threading;

class C# Tutorial

{

    static int c = 0;

    static object s = new object();

    static void Inc()

    {

        for ( int i = 0; i < 1000; i++ )

        {

            lock (s) // Synchronization occurs

            {

                c++;

            }

        }

    }

    static void Main()

    {

        Thread m1 = new Thread(Inc);

        Thread m2 = new Thread(Inc);

        m1.Start();

        m2.Start();

        m1.Join();

        m2.Join();

        Console.WriteLine("The final counter value is " + c );

    }

}

Output:

Output

The final counter value is 2000

Explanation:

In this example scenario, we've selected two threads, m1 and m2, where both threads simultaneously run the Inc function. This function increments a common counter variable c by 1, a thousand times each. Subsequently, we apply a lock on object s to guarantee that only one thread can modify the value at any given time. Upon the conclusion of both thread operations, the resulting counter value of 2000 is presented on the display.

Thread Synchronization Using a Lock

In C#, the lock keyword is a popular technique for synchronizing threads. It is frequently employed to prevent concurrent access to shared resources. When multiple threads attempt to interact with the same data concurrently, it can result in problems like race conditions, causing incorrect or unpredictable outcomes. By using the lock statement, it guarantees that only one thread can run it exclusively.

C# Thread Synchronization Example using Lock

Now, we will proceed with an example to explain the concept of thread synchronization using a lock in C#.

Example

Example

using System;

using System.Threading;

class C# Tutorial

{	

    static int c = 0;

    static object lockObj = new object ();

    static void Incrementer()

    {

        for (int i = 1; i <= 5; i++)

        {

            lock  (lockObj)  // Lock to protect shared resource

            {

                c++;

            Console.WriteLine(Thread.CurrentThread.Name + " -> Counter: " + c);

            }

            Thread.Sleep(200); 

        }

    }

    static void Main()

    {

        Thread t1 = new Thread(Incrementer);

        Thread t2 = new Thread(Incrementer);

        t1.Name = "Thread 1";

        t2.Name = "Thread 2";

        t1.Start();

        t2.Start();

        t1.Join();

        t2.Join();

        Console.WriteLine("\nFinal Counter Value: " + c);

    }

}

Output:

Output

Thread 1 -> Counter: 1

Thread 2 -> Counter: 2

Thread 1 -> Counter: 3

Thread 2 -> Counter: 4

Thread 1 -> Counter: 5

Thread 2 -> Counter: 6

Thread 1 -> Counter: 7

Thread 2 -> Counter: 8

Thread 1 -> Counter: 9

Thread 2 -> Counter: 10



Final Counter Value: 10

Explanation:

In the scenario described, we have considered two threads, Thread1 and Thread2, and initiated the Incrementer function. Here, both threads aim to modify the shared counter variable c. Subsequently, the lock(lockObj) construct is employed to guarantee that only one thread is active within the critical section responsible for incrementing and displaying the counter. Each thread increments the counter five times, resulting in a total value of 10. Lastly, the Console.WriteLine function is utilized to display the output.

C# Thread Synchronization Using Monitor Class

In C#, the Monitor class offers a versatile approach to thread synchronization. It is frequently employed to manage shared resources' access by multiple threads, guaranteeing that only one thread operates within the program's critical section at any given time. While resembling the lock keyword in functionality, the Monitor class includes additional functionalities like Wait, Pulse, and PulseAll for enhanced control over synchronization.

C# Thread Synchronization Example Using Monitor Class

Let's consider a basic illustration to showcase how the Monitor class is employed for thread synchronization in the C# programming language.

Example

Example

using System;

using System.Threading;

class C# Tutorial

{

    static int c = 0;

    static object lockObj = new object();

    static void Incrementer()

    {

        for (int i = 1; i <= 5; i++)

        {

            Monitor.Enter(lockObj);   // Begin synchronization

            try

            {

                c++;

                Console.WriteLine(Thread.CurrentThread.Name + " -> Counter: " + c);

            }

            finally

            {

                Monitor.Exit(lockObj); // Release lock

            }

            Thread.Sleep(200);

        }

    }

    static void Main()

    {

        Thread t1 = new Thread(Incrementer);

        Thread t2 = new Thread(Incrementer);

        t1.Name = "The Thread 1";

        t2.Name = "The Thread 2";

        t1.Start();

        t2.Start();

        t1.Join();

        t2.Join();

        Console.WriteLine("\nThe final counter value is " + c);

    }

}

Output:

Output

The Thread 1 -> Counter: 1

The Thread 2 -> Counter: 2

The Thread 1 -> Counter: 3

The Thread 2 -> Counter: 4

The Thread 1 -> Counter: 5

The Thread 2 -> Counter: 6

The Thread 1 -> Counter: 7

The Thread 2 -> Counter: 8

The Thread 1 -> Counter: 9

The Thread 2 -> Counter: 10



The final counter value is 10

Explanation:

In the example provided, we employ the Monitor.Enter and Monitor.Exit techniques to guarantee that only a single thread modifies the counter concurrently. In this scenario, both threads run the incrementer function simultaneously. Subsequently, the Monitor.Enter operation secures the shared object, allowing for the update. Following the modification, the Monitor.Exit operation frees the lock. To conclude, we utilize the Console.WriteLine function to display the result.

Using Mutex in Synchronized threads in C#

In C# programming, a Mutex is employed to regulate thread access, particularly when threads are associated with distinct programs. Unlike a lock that operates within a single application, a mutex facilitates thread synchronization among various applications.

C# Thread Synchronization Example Using Mutex

Let's consider an example to showcase the use of a mutex for thread synchronization in C#.

Example

Example

using System;

using System.Threading;

class C# Tutorial

{

    static Mutex mutex = new Mutex();

    static int count = 0;

    static void Inc()

    {

        mutex.WaitOne();

        count++;

        Console.WriteLine( Thread.CurrentThread.Name + " -> " + count );

        mutex.ReleaseMutex();

    }

    static void Main()

    {

        Thread t1 = new Thread(Inc);

        Thread t2 = new Thread(Inc);

        t1.Name = "The Thread 1";

        t2.Name = "The Thread 2";

        t1.Start();

        t2.Start();

    }

}

Output:

Output

The Thread 1 -> 1

The Thread 2 -> 2

Explanation:

In the scenario described, we have instantiated the mutex object and an integer variable named count. Upon a thread invoking the mutex.WaitOne method, it gains possession of the mutex and accesses the critical section to guarantee that only a single thread can modify the count concurrently. Any additional threads are required to remain in a waiting state until the mutex becomes available. Subsequent to the task's completion, the thread triggers the mutex.ReleaseMutex function to release the mutex lock. To display the results, we employ the Console.WriteLine technique.

Using a Semaphore in Thread Synchronization

In C# programming, a semaphore is employed to control the access of shared resources by multiple threads, enabling a defined number of threads to access the critical section simultaneously.

C# Thread Synchronization Example using Semaphore:

Here, we will use an example to illustrate how Semaphores are utilized for synchronization in C#.

Example

Example

using System;

using System.Threading;

class C# Tutorial

{

    static Semaphore sm = new Semaphore(2, 2);

    static void Work()

    {

        Console.WriteLine( Thread.CurrentThread.Name + " is waiting... " );

        sm.WaitOne ();

        Console.WriteLine( Thread.CurrentThread.Name + " entered the critical section. " );

        Thread.Sleep( 1000 );

        Console.WriteLine( Thread.CurrentThread.Name + " is leaving the critical section. " );

        sm.Release();

    }

    static void Main()

    {

        for ( int i = 1; i <= 5; i++ )

        {

            Thread t = new Thread ( Work );

            t.Name = " Thread " + i;

            t.Start();

        }

    }

}

Output:

Output

Thread 2 is waiting...

Thread 3 is waiting...

Thread 5 is waiting...

Thread 1 is waiting...

Thread 4 is waiting...

Thread 1 entered the critical section.

Thread 2 entered the critical section.

Thread 1 is leaving the critical section.

Thread 2 is leaving the critical section.

Thread 5 entered the critical section.

Thread 4 entered the critical section.

Thread 5 is leaving the critical section.

Thread 4 is leaving the critical section.

Thread 3 entered the critical section.

Thread 3 is leaving the critical section.

Explanation:

In the previous instance, the Semaphore is established with 2 slots, enabling the admission of only two threads concurrently into the critical segment. Upon initiation of each thread, a message indicating it is in a waiting state is displayed. Subsequently, the sm.WaitOne function is employed to secure a slot within the semaphore. If a slot is unoccupied, the thread proceeds into the critical section. In cases where no slots are vacant, the thread remains in a waiting state until a slot is freed up. The sm.Release function is invoked to release the thread from the semaphore. Lastly, the Console.WriteLine function is utilized to display the desired output.

Using a SemaphoreSlim in Thread Synchronization

In C# programming, a SemaphoreSlim serves as a lightweight synchronization mechanism crafted for in-process thread coordination. It provides enhanced efficiency when contrasted with a Semaphore and finds particular utility in managing asynchronous operations.

C# Thread Synchronization Example using a SemaphoreSlim

In this instance, we will use a practical example to showcase the SemaphoreSlim for synchronization in C#.

Example

Example

using System;

using System.Threading;

class C# Tutorial

{

    static SemaphoreSlim sm = new SemaphoreSlim (2);

    static void Work()

    {

        Console.WriteLine(Thread.CurrentThread.Name + " is waiting... ");

        sm.Wait();

        Console.WriteLine(Thread.CurrentThread.Name + " entered. ");

        Thread.Sleep( 1000 );

        Console.WriteLine(Thread.CurrentThread.Name + " is leaving. ");

        sm.Release();

    }

    static void Main()

    {

        for (int i = 1; i <= 5; i++)

        {

            Thread t = new Thread(Work);

            t.Name = "The Thread " + i;

            t.Start();

        }

    }

}

Output:

Output

The Thread 3 is waiting... 

The Thread 1 is waiting... 

The Thread 3 entered. 

The Thread 5 is waiting... 

The Thread 5 entered. 

The Thread 2 is waiting... 

The Thread 4 is waiting... 

The Thread 3 is leaving. 

The Thread 5 is leaving. 

The Thread 2 entered. 

The Thread 1 entered. 

The Thread 2 is leaving. 

The Thread 4 entered. 



The Thread 1 is leaving. 

The Thread 4 is leaving.

Explanation:

In the scenario described, a SemaphoreSlim restricts access to the critical section to two threads concurrently. Upon initiation, every thread announces its wait status. Subsequently, the Wait method is employed to acquire a slot. If the two threads have already been processed, any additional threads must wait until a slot becomes available. Upon receiving authorization, a thread confirms its entry. Upon exiting, the Release method is invoked to free up the thread.

Conclusion

In summary, synchronization of threads plays a crucial role in guaranteeing secure and foreseeable operation when handling multiple threads that interact with common resources. This practice averts conflicts and minimizes errors, ensuring seamless program execution amidst concurrent thread activities.

C# Thread Synchronization FAQs

1) What is thread synchronization in C#?

In C#, synchronization of threads is a technique frequently employed in multi-threaded development to control the access of multiple threads to shared resources. This practice helps maintain the consistency of data and prevents race conditions in programs with multiple threads.

2) What is a Mutex in C#?

In C#, a Mutex serves as a synchronization tool for threads, enabling exclusive access for one thread to a shared resource or critical section at a time. It further facilitates synchronization between multiple processes.

3) What is SemaphoreSlim in C#?

In C#, a SemaphoreSlim serves as a nimble synchronization mechanism crafted for in-process thread coordination. It provides superior efficiency when contrasted with a Semaphore and finds particular utility in handling asynchronous operations.

4) What is the Monitor class in synchronization in C#?

In C#, the Monitor class offers a more versatile approach to thread synchronization. It functions similarly to a lock but includes additional functionalities like Wait, Pulse, and PulseAll to enhance thread control.

5) What is a thread in C#?

In C#, a thread stands as the most basic unit of execution within a program, signifying an independent flow of execution. Enabling concurrent task execution, threads facilitate parallel processing, thereby enhancing overall performance through optimal CPU utilization.

Input Required

This code uses input(). Please provide values below: