Mutex vs Semaphore vs Monitor vs SemaphoreSlim using example

发布于 2022-01-20  44346 次阅读


from here

Introduction

Hello Friends..! In this article document we will understand how to work on multi-thread environment using mutex, semaphore, monitor and semaphoreslim. Before reading this article I strongly suggest you to read our previous c-sharp articles on threading and types of threading and thread pooling. For more information threading click here and for thread pooling see here.

Before we talk about mutex, semaphore, monitor and semaphoreslim and their differences let's understand what is thread safety.

What is Thread Safe in C#

If an object behaves abnormally in a multi-threaded environment then that thread is not thread safe. For example : Let's say if we run a divide function with random numbers in a multi-threaded environment since it is running in a multi-threaded environment there could be possibility that two threads are executing divide calculation same time using random numbers can create system.dividebyzeroexception error. If these types of errors occurs in a multi-thread environment that means thread is not thread safe. So it means we should take some precaution while working in a multi-threaded environment atleast those lines of code where we feel code can behave abnormally. The precaution or thread safety can be achieved using proper thread synchronization techniques. Currently available thread synchronization techniques in c-sharp are Lock/Monitor, Mutex, Semaphore and SemaphoreSlim.

Lock vs Monitor

Lock keyword is very important in multithreading. It ensures thread safety and in a multithreading environment it ensures only one thread to be executed in given moment of time. In simple words whatever code is enclosed in lock scope only one thread can enter in that lock scope and other threads have to wait till entered thread completes its work/ executes its work.

C# Lock Example

Here in this example we will first execute a simple console application divide function in a multi-thread environment without thread safety to check the output exception error. We will take up the same example as we explained in the thread safety section.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static int num1 = 0;
        static int num2 = 0;
        static Random random = new Random();
        static void Main(string[] args)
        {

            Thread obj = new Thread(Divide);
            obj.Start();

            Divide();
        }

        static void Divide()
        {
            
            for (int i = 0; i <= 5000; i++)
            {

                try
                {
                    //Choosing random numbers between 1 to 5
                    num1 = random.Next(1, 5);
                    num2 = random.Next(1, 5);


                    //Dividing
                    double ans = num1 / num2;


                    //Reset Variables
                    num1 = 0;
                    num2 = 0;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }
        }
    }
}

Above code example is a simple console application example which executes Divide function in a multi-threaded way. Divide method loops 5000 times and takes two random numbers between 1 to 5 and divides themselves. When we ran this program we got the following error i.e. System.DividebyZeroException this because the method Divide is running in a multi-thread process i.e. static void main is the Main thread or Parent thread and inside that we have created one more thread object i.e. child thread.

So while running this program there could be possibility that while one thread is dividing other thread may be resetting the value to 0. Because of that it has created an DividebyZeroException error.

This is what we were talking about in the thread safety that is abnormal behave of an object in multithreading. To resolve this we can use any thread synchronization techniques but here as per situation best approach to use LOCK/Monitor.

To apply lock we need to use "LOCK" keyword as shown in below snippet of code.

 
static readonly object _object = new object();

lock (_object)
{
         //Choosing random numbers between 1 to 5
         num1 = random.Next(1, 5);
         num2 = random.Next(1, 5);

         //Dividing
         double ans = num1 / num2;

         //Reset Variables
         num1 = 0;
         num2 = 0;
}

Monitor in c# example

Lock keyword is just a shot-form of Monitor. So usage of monitor class or lock is one and same. But here we will also show you same code example using Monitor class. Its pretty simple whatever code you want to protect at starting point say "Monitor.Enter" and at ending point say "Monitor.Exit" as shown in below code.

 

Monitor.Enter(_object);

  //Choosing random numbers between 1 to 5
         num1 = random.Next(1, 5);
         num2 = random.Next(1, 5);

         //Dividing
         double ans = num1 / num2;

         //Reset Variables
         num1 = 0;
         num2 = 0;

Monitor.Exit(_object);

So as you see friends LOCK and MONITOR ensures thread safety and prevent code from showing undesirable results.

Lock vs Mutex

Locks/Monitors ensures the thread safety which are in process that is threads which are generated by an application (Internal threads) it does not have any control over the threads which are coming from outside of an application. Locks/Monitors provides safety againts the threads generated by an application.

Mutex ensures the thread safety which are out process that is threads which coming from outside of an application (External threads). Mutex provides safety againts the external threads.

In a multiple instance of an application external threads are created so to ensure thread safety from an external threads we need to apply mutex. Let's see a simple example how to ensure thread safety from external threads.

Mutex example in C#

 

class Program
{
        static Mutex m1 = new Mutex(true, "Questpond");
        
        static void Main(string[] args)
        {

            if (IsInstance() == true)
            {
                
                Console.WriteLine("New Instance created...");
            }
            else
            {

                Console.WriteLine("Instance already acquired...");
            }

            Console.ReadLine();
        }

        static bool IsInstance()
        {

            if (m1.WaitOne(5000,false) == false)
            {
                return false;
            }
            else
            {
                return true;
            }
          
        }
}

In the above code we have created a boolean function called "IsInstance" which checks whether any other instance is running or not and that we have achieved using "WaitOne" boolean function which blocks the current thread unit waithandle receives the signal. Here in our demonstration WaitOne waits for 5 secs to receive signal from waithandle if no signal received till 5 secs it will automatically return false. If its a new thread then automatically returns true.

To see the real time scenario follow these steps.

First build the complete source code

Second Go to application containing folder

Third EXE will be generated in the BIN folder so open BIN folder.

Fourth Click on that exe and create external threads.

Fifth Do not close that previous one exe.

Sixth By keeping that previous command line exe click on the same exe file to open another new command line exe.

Seventh Keep both command line exe and open more 2 or 3 times open new command line exe.

Output

First opened command line will display message as : New Instance created

All other opened command line will display messages as : Instance already acquired

Conclusion : It is been concluded that Mutex helps us to identify whether an application is acquired by an external thread or not.

Mutex vs Semaphore

Mutex helps us to identify whether an application is acquired by an external thread or not and It allows only one single thread to enter to execute a particular task. It means mutex allows only one single external thread to enter and execute its task and same ensuring thread safety.

Semaphore you can call its an advance version of mutex with additional features. Semaphore is also helps us to work with external threads and identifying whether an application is acquired by an external thread or not. But unlike mutex Semaphore allows one or more threads to enter to executes their task with thread safety. Best feature of semaphore that we can limit those number of threads to enter.

Semaphore example in c#

Let's see a simple example of using semaphore in c#.

 

    class Program
    {
      
        static Semaphore s1 = new Semaphore(2, 2, "SemaphoreQuestpond");
        
        static void Main(string[] args)
        {

            if (IsInstance() == true)
            {
                
                Console.WriteLine("New Instance created...");
            }
            else
            {

                Console.WriteLine("Instance already acquired...");
            }

            Console.ReadLine();
        }

        static bool IsInstance()
        {

            if (s1.WaitOne(5000, false) == false)
            {
                return false;
            }
            else
            {
                return true;
            }
          
        }
    }

As you saw in our above code we have created a semaphore object "s1" limit of 2 threads with a name "SemaphoreQuestpond".

Now we will examine above example whether semaphore allows us to pass two external threads at a same time keeping thread safety.

First build the complete source code

Go to bin folder and click on exe file twice

Without closing both command file exe file click on exe file again

Output

First two opened command line will display message as : New Instance created

All other opened command line will display messages as : Instance already acquired

Conclusion : It is been concluded that semaphore helps us to identify whether an application is acquired by an external thread or not and at a same time allows to pass multiple threads (Limit) and keeps thread safety.

Monitor vs SemaphoreSlim

Lock is just short form of monitor. Monitor ensures thread safety with internal threads whatever code is enclosed between monitor enter and monitor exit only one thread can pass and executes its task.

SemaphoreSlim is an advance version of Monitor. SemaphoreSlim ensures the thread safety with internal threads but between scope of SemaphoreSlim it allows us to pass one or more threads to pass and executes their task. SemaphoreSlim also provides you an advance limit where you can limit the number of threads for an execution.

Watch Mutex, Semaphore and SemaphoreSlim Tutorial Video By Questpond

http://player.vimeo.com/video/51435705

Hey friends..! This is all about mutex, semaphore, semaphoreslim and monitor/lock. If you folks have any doubt or query kindly drop a mail or let me know your thoughts via comment section.

If you found this article helpful. Kindly share it with your friends.


既不回头,何必不忘。既然无缘,何须誓言。