Lock Statement In C# – When To Use Lock keyword

This post series we will go through the understanding of the lock statement, monitor, mutex, and semaphore available in C#.

All of these classes provide a synchronization mechanism to protect the shared code or resources in a multithreaded application.

Let’s first understand the lock statement in C#.

C# Lock Statement

Lock Statement: In C#, the  lock  is a synchronization mechanism that allows only one thread to access a specified piece of code or a common field at a time.

It is mainly used to achieve an exclusive lock to avoid inconsistent output while reading and writing to a common variable in a multithreaded environment.

Generally, the  lock  keyword is placed around a critical section of code, where we want to allow only one thread to access the resource at a time.
All the other threads have to wait and halt the execution until the locked object is released.

Pictorial representation of mutual exclusion of lock

Lock in C#

Lock Syntax In C#

Lock (expression) { statement block }

In C#, the following is the syntax to use the lock keyword.

lock (obj)
 {
   // Critical code section
 }

Example of Lock Statement In C#

In the following example, we are using the lock keyword around a critical section of code. Which allows only one thread to enter and execute the code at a time.

using System;
using System.Threading;
namespace LockStatementDemo
{
    class Program
    {
        static readonly object _lock = new object();
        static void Main(string[] args)
        {          
         // Creating threads
         Thread thread1 = new Thread(PrintCharacter);
         Thread thread2 = new Thread(PrintCharacter);
        // Executing the tasks
         thread1.Start();
         thread2.Start();

         Console.ReadLine();
      
        }
        public static void PrintCharacter()
        {
            string strArray = "Hello World";
            lock (_lock)
            {
                for (int i = 0; i < strArray.Length; i++)
                {
                    Console.Write($"{strArray[i]}");
                    //Pausing the thread execution for 2 seconds
                    Thread.Sleep(TimeSpan.FromSeconds(2));
                }
            }
            Console.Write(" ");
        }

    }
}

In the above code, we have created two separate threads thread1, and thread2 in the main method to call the static method PrintCharacter simultaneously.

Here, we are using the lock statement on the variable of type object called ‘_lock’ to acquire the exclusive lock on the  for loop  statement.
It will allow only one thread to enter into the critical section to execute the code at a time.

Let’s run the above program to see the result.

Multithreading using lock
The output of the above C# program.

In the above result, we can see that the lock statement blocked the second thread to execute the code until the first thread released the object.

C# – Multithreading Without Lock Statement

In the following example, multiple threads are executing the critical section of code simultaneously without using the lock statement.

using System;
using System.Threading;
namespace Multithreading
{
    class Program
    {    
        static void Main(string[] args)
        {          
         // Creating threads
         Thread thread1 = new Thread(PrintCharacter);
         Thread thread2 = new Thread(PrintCharacter);
        // Executing the tasks
         thread1.Start();
         thread2.Start();

         Console.ReadLine();
      
        }
        public static void PrintCharacter()
        {
            string strArray = "Hello World";
           
                for (int i = 0; i < strArray.Length; i++)
                {
                    Console.Write($"{strArray[i]}");
                    //Pausing the thread execution for 2 seconds
                    Thread.Sleep(TimeSpan.FromSeconds(2));
                }
           
            Console.Write(" ");
        }
    }
}

Multithreading without lock keyword
The output of the above program

In the above result, we can see that both thread1 and thread2 are executing the same piece of code simultaneously.

That’s why we use the lock statement to achieve an exclusive lock to avoid inconsistency in the output while reading and writing to a common variable in a multithreaded environment.

In C#, the lock statement internally wraps the Monitor.Enter and Monitor.Exit methods, with additional try/finally block.

 lock   Monitor.Enter  +  Monitor.Exit +  try/finally 

Use Lock on a reference type rather than the value type

In the above program, we are passing an object instance as a parameter in the lock statement.

But what if we try to pass a value type variable in the lock statement rather than an object type.

unfortunately, The C# compiler will throw a compile-time error message like:
‘int’ is not a reference type as required by the lock statement.

 class Program
    {
        static int _lock;
        static void ReadFile()
        {
            lock (_lock)
            {
                // Code
            }
        }
     }

Avoid using the lock statement in the following situation

Value type: Use Lock on a reference type object rather than value type otherwise you will get a compile-time error.

this keyword: Use private reference type variable rather than this keyword (Example: lock(this)) to avoid the deadlock situation, where multiple threads wait for the release of the same object.

Using ‘this’ as a lock expression is also a bad practice as it will block the entire object,
All the other parts of the code will be blocked and wait to execute for no reason and may cause the performance issue.

Using ‘this’ can be problematic as the instance can be accessed publicly and can access by multiple threads.

Type: Don’t lock the type objects ( Example: lock (typeof(MyClass)) as it can be accessed and shared across the application domain, which might invite deadlock situations and can cause poor performance.

The safest way is to only lock private reference type objects.

String object: Avoid using the lock statement on string objects because interned strings are essentially global, and can be locked by other threads without you knowing this and may cause the deadlock situation.

Conclusion

The  lock  statement is basically used to protect a shared resource from being access by the multiple threads in a multithreaded environment.

It allows only one thread to access a  critical section of code at a time to avoid the race condition.

Hope you enjoyed this post. Thanks for visiting.

Leave a Reply

Your email address will not be published. Required fields are marked *

eighteen − 11 =