In this post, we will see how to use Lock and Mutex for obtaining a lock in a thread.
We start creating a Console application where, we will add a class called Methods:
[METHODS.CS]
using System;
using System.Threading;
namespace LockAbortThread
{
public class Methods
{
public void RunMethods()
{
Thread firstThread = new Thread(MethodOne);
Thread secondThread = new Thread(MethodOne);
firstThread.Start();
secondThread.Start();
}
private void MethodOne()
{
for (int i = 1; i < 6; i++)
{
Console.WriteLine($"Output: {i} ---- ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
}
}
}
}
Then, we modify the Program file in order to run RunMethods():
[PROGRAM.CS]
using System;
namespace LockAbortThread
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Start threads");
Methods objMethods = new Methods();
objMethods.RunMethods();
}
}
}
Now, if we run the application, this will be the result:
In this example, if we would like to run the two Threads in sequence, we could either use the method Join() (we have seen it in the post: Multithreading – Joining Threads) or, we could use the keyword LOCK.
With the keyword LOCK, we are sure that no thread will enter a critical section of code while another thread is in that section:
[METHODS.CS]
using System;
using System.Threading;
namespace LockAbortThread
{
public class Methods
{
public void RunMethods()
{
Thread firstThread = new Thread(MethodOne);
Thread secondThread = new Thread(MethodOne);
firstThread.Start();
secondThread.Start();
}
private void MethodOne()
{
lock(this)
{
for (int i = 1; i < 6; i++)
{
Console.WriteLine($"Output: {i} ---- ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
}
}
}
}
}
Now, if we run the application, this will be the result:
LOCK it is used in simple situations because, it doesn’t have any parameters or methods that could help us to resolve for example a deadlock.
A deadlock is a situation where, two or more threads, are frozen in their execution because they are waiting for each other to finish.
In order to avoid a deadlock, we can use Mutex that is synchronization primitive that can also be used for interprocess synchronization.
It works like a Lock because, it acquires an exclusive lock on a shared resource from concurrent access but, it works across multiple processes.
For other information: Microsoft web site.
We can use Mutex is this simple way:
using System;
using System.Threading;
namespace LockAbortThread
{
public class Methods
{
private static Mutex mutex = new Mutex();
public void RunMethods()
{
Thread firstThread = new Thread(MethodOne);
Thread secondThread = new Thread(MethodOne);
firstThread.Start();
secondThread.Start();
}
private void MethodOne()
{
mutex.WaitOne();
for (int i = 1; i < 6; i++)
{
Console.WriteLine($"Output: {i} ---- ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
}
mutex.ReleaseMutex();
}
}
}
If we run the application, this will be the result:
We can see that it works fine but, in reality, this implementation has the same problem with the deadlock like a LOCK.
In order to avoid a deadlock with Mutex, we can insert a timeout.
If Mutex doesn’t run after a specific number of seconds, the system will stop the execution of the application, avoiding in this way a deadlock:
using System;
using System.Threading;
namespace LockAbortThread
{
public class Methods
{
private static Mutex mutex = new Mutex();
public void RunMethods()
{
Thread firstThread = new Thread(MethodOne);
Thread secondThread = new Thread(MethodOne);
firstThread.Start();
secondThread.Start();
}
private void MethodOne()
{
// the value is in Milliseconds
// if after 6 seconds, the thread cannot run, it will stop to run
if(mutex.WaitOne(6000))
{
for (int i = 1; i < 6; i++)
{
Console.WriteLine($"Output: {i} ---- ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
}
mutex.ReleaseMutex();
}
else
{
// System is busy and application cannot run the Thread
Console.WriteLine("Application can run a Thread. The system is busy.");
}
}
}
}
If we run the application, this will be the result:
But now, if we set the Timeout to 5 seconds, we will see that the application will stop after the first thread because it tooks more of 5 seconds to complete the first method:
private void MethodOne()
{
// the value is in Milliseconds
// if after 5 seconds, the thread cannot run, it will stop to run
if(mutex.WaitOne(5000))
{
for (int i = 1; i < 6; i++)
{
.
..
...
}
If we run the application, this will be the result: