C# Threads And Resource Locking

C# Code Snippets Thread Synchronization with Monitor and Lock
Share:

About

In this code snippet, we will take a look at thread synchronization with Monitor and Lock in C#.

In this post, I showed how to start new threads to execute code in parallel. However, when you start using threads what can happen is that two threads will access the same resources at the same time. One thread might modify a variable before another thread is done executing. This can, of course, cause errors.

To prevent this from happening we have to lock the resources in question and prevent other threads from using them until the current thread is done executing. We can do this by either using the Monitor class or the lock keyword. These two approaches are functionally identical. The only difference (as you will be able to see in the code below) is that lock is more compact and easier to use.

You will notice that we have to provide Monitor.Start/Monitor.End and lock an object to lock on. The object being locked is just some “random” object we create to lock on(more specifically it’s synchronization block). Any properties of that object can actually still be accessed by another thread while the object is locked. The code between Monitor.Enter and Monitor.Exit or inside the lock code block is the actual code that is locked and is not allowed to be modified by another thread. Kind of silly and confusing isn’t it? But that is how it works.

Let’s have a look at the code below to see how to lock resources.

Code:

using System;
using System.Threading;

namespace ThreadMonitor
{
    class Program
    {
        static void Main(string[] args)
        {
            WaterTank tank = new WaterTank();
                
            //Thread unsafe code. Mutiple threads will be accessing same class property. 
            Thread th1 = new Thread(delegate () { tank.dispenseWater(400); });
            Thread th2 = new Thread(delegate () { tank.dispenseWater(500); });
            Thread th3 = new Thread(delegate () { tank.dispenseWater(400); });
            Thread th4 = new Thread(delegate () { tank.dispenseWater(500); });
           
            /*
            //Thread safe code.
            Thread th1 = new Thread(delegate () { tank.dispenseWaterThreadSafe(400); });
            Thread th2 = new Thread(delegate () { tank.dispenseWaterThreadSafe(500); });
            Thread th3 = new Thread(delegate () { tank.dispenseWaterThreadSafe(400); });
            Thread th4 = new Thread(delegate () { tank.dispenseWaterThreadSafe(500); });
            */
            
            /*
            //Thread safe code.
            Thread th1 = new Thread(delegate () { tank.dispenseWaterThreadSafeLock(400); });
            Thread th2 = new Thread(delegate () { tank.dispenseWaterThreadSafeLock(500); });
            Thread th3 = new Thread(delegate () { tank.dispenseWaterThreadSafeLock(400); });
            Thread th4 = new Thread(delegate () { tank.dispenseWaterThreadSafeLock(500); });      
            */

            //Start threads.
            th1.Start();
            th2.Start();
            th3.Start();
            th4.Start();

            Console.ReadLine();
        }
    }

    class WaterTank
    {
        public int waterQuantity { get; set; }

        public WaterTank()
        {
            //On instantiation of the class "fill" up the tank.
            waterQuantity = 1000;
        }

        public void dispenseWater(int liters)
        {
            //After putting this code in the lock code block we no longer get the previous.
            if ((waterQuantity - liters) > 0)
            {
                //Simulate doing some time consuming work.
                Thread.Sleep(1000);

                //Update water quantity.
                waterQuantity = waterQuantity - liters;

                //Display dispensed and remainig water.
                Console.WriteLine("Dispensed " + liters + " liters of water. " + waterQuantity + " liters remaining.");
            }
            else
            {
                Console.WriteLine("There is not enough water.");
            }
        }



        //Create an object to lock on.
        private Object quantityLock = new Object();

        public void dispenseWaterThreadSafe(int liters)
        {
            //Lock the object and prevent any other code from executing the code below until the current thread is done executing.
            Monitor.Enter(quantityLock);
            try
            {
                //////////////////////////////////////////////////////////////////////////////
                //The object being locked is just something to lock on and any properties of that object can actually still be accessed by another thread. 
                //The code in here (between Monitor.Enter and Monitor.Exit) is the actual code that is locked and is not allowed to be modified by another thread.
                //Kind of silly and confusing isn't it? But that is how it works.

                if ((waterQuantity - liters) > 0)
                {
                    //Simulate doing some time consuming work.
                    Thread.Sleep(1000);

                    //Update water quantity.
                    waterQuantity = waterQuantity - liters;

                    //Display dispensed and remainig water.
                    Console.WriteLine("Dispensed " + liters + " liters of water. " + waterQuantity + " liters remaining.");
                }
                else
                {
                    Console.WriteLine("There is not enough water.");
                }

                ////////////////////////////////////////////////////////////////////////////
            }
            finally
            {
                //Release the lock and allow other threads to execute the code above.
                Monitor.Exit(quantityLock);
            }
        }

        public void dispenseWaterThreadSafeLock(int liters)
        {
            //The lock keyword is just a more compact and easier way of doing what we did in the method dispenseWaterThreadSafe() with Monitor.
            lock (quantityLock)
            {
                if ((waterQuantity - liters) > 0)
                {
                    //Simulate doing some time consuming work.
                    Thread.Sleep(1000);

                    //Update water quantity.
                    waterQuantity = waterQuantity - liters;

                    //Display dispensed and remainig water.
                    Console.WriteLine("Dispensed " + liters + " liters of water. " + waterQuantity + " liters remaining.");
                }
                else
                {
                    Console.WriteLine("There is not enough water.");
                }
            }
        }
    }
}

Resulting output:

Thread Unsfe
c# thread safe code resulting output.jpg
Thread Safe
Share:

Leave a Reply

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

The following GDPR rules must be read and accepted:
This form collects your name, email and content so that we can keep track of the comments placed on the website. For more info check our privacy policy where you will get more info on where, how and why we store your data.

Advertisment ad adsense adlogger