Back to: Dot Net Interview Questions and Answers
Multithreading and Deadlock Interview Questions and Answers in C#
In this article, I am going to discuss the most frequently asked Multithreading and Deadlock Interview Questions and Answers in C#. Please read our previous article where we discussed the most frequently asked Delegates Interview Questions in C# with Answers. As part of this article, we are going to discuss the following Multithreading and Deadlock Interview Questions and Answers in C#.
- What are the Thread and Process?
- What is the difference between Process and Thread?
- Why do we need Multi-threading in our project?
- What are the advantages and disadvantages of multithreading in C#?
- How can we create a Thread in C#?
- Why does a delegate need to be passed as a parameter to the Thread class constructor?
- How to pass parameter in Thread?
- Why do we need a ParameterizedThreadStart delegate?
- When to use ParameterizedThreadStart over ThreadStart delegate?
- How to pass data to the thread function in a type-safe manner?
- How to retrieve the data from a thread function?
- What is the difference between Threads and Tasks?
- What is the Significance of Thread.Join and Thread.IsAlive functions in multithreading?
- What happens if shared resources are not protected from concurrent access in a multithreaded program?
- How do protect shared resources from concurrent access?
- What are the Interlocked functions?
- What is a Lock?
- What is the Difference between Monitor and lock in C#?
- Explain why and how a deadlock can occur in multithreading with an example?
- How to resolve a deadlock in a multithreaded program?
- What is AutoResetEvent and how it is different from ManualResetEvent?
- What is the Semaphore?
- Explain Mutex and how it is different from other Synchronization mechanisms?
- What is the Race condition?
- How can you share data between multiple threads?
- What are Concurrent Collection Classes?
- What is synchronization and why it is important?
- Explain the four necessary conditions for Deadlock?
- What is LiveLock?
What are the Thread and Process?
Process – Process is something that the operating system uses to execute a program by providing the resources required. Each process has a unique process id associated with it. We can view the process within which a program is running using the windows task manager.
Thread – A Thread is a lightweight process that is responsible for executing application code. A process has at least one thread which is commonly called the main thread which actually executes the application code. A single process can have multiple threads.
Every application by default contains one thread to execute the program and that thread is known as the main thread. So every program by default is a single-threaded model.
What is the difference between Process and Thread?
This is one of the most frequently asked Multithreading Interview Questions in C#. A process is started when you start an Application. The process is a collection of resources like virtual address space, code, security contexts, etc. A process can start multiple threads. Every process starts with a single thread called the primary thread. You can create n number of threads in a process. Threads share the resources allocated to the process. A process is the parent and threads are his children.
Why do we need Multi-threading in our project?
This is one of the most frequently asked Multithreading Interview Questions in C#.NET. Let us discuss this question. Multi-threading is used to run multiple threads simultaneously. Some main advantages are:
- You can do multiple tasks simultaneously. For e.g. saving the details of the user to a file while at the same time retrieving something from a web service.
- Threads are much more lightweight than processes. They don’t get their own resources. They used the resources allocated to a process.
- Context-switch between threads takes less time than process.
What are the advantages and disadvantages of multithreading?
I think this MultiThreading Interview Question is the most asked interview question in the dot net. So let us discuss the advantages and disadvantages
Advantages of multithreading:
- To maintain a responsive user interface
- It makes efficient use of processor time while waiting for I/O operations to complete.
- To split large, CPU-bound tasks to be processed simultaneously on a machine that has multiple CPUs/cores.
Disadvantages of multithreading:
- On a single-core/processor machine threading can affect performance negatively as there is overhead involved with context-switching.
- Have to write more lines of code to accomplish the same task.
- Multithreaded applications are difficult to write, understand, debug, and maintain.
Please Note: Only use multithreading when the advantages of doing so outweigh the disadvantages.
How can we create a Thread in C#?
To create a THREAD, we need to create an instance of Thread class (Thread class provided by System.Threading namespace) and to its constructor, we have to pass the function name as a parameter that we want the thread to execute. Then we need to call the start method of the Thread class.
Let us understand this with an example as shown below.
In the above example, the following statement does the same
Thread T1 = new Thread(Number.PrintNumbers); T1.Start();
We can rewrite the above line using the ThreadStart delegate as shown below.
Thread T1 = new Thread(new ThreadStart(Number.PrintNumbers));
T1.Start();
Why does a delegate need to be passed as a parameter to the Thread class constructor?
As we know the purpose of creating a Thread is to execute a function. We also know that a delegate is a type-safe function pointer meaning it points to a function that the thread has to execute. In short, all threads require an entry point to start execution. Any thread that we create will need an explicitly defined entry point i.e. a pointer to the function from where they should begin execution. So threads always require a delegate.
In the code below, we are not explicitly creating the ThreadStart delegate, then how is it working here?
Thread T1 = new Thread(Number.PrintNumbers);
It’s working in spite of not creating the ThreadStart delegate explicitly because the framework is doing it automatically for us. We can also rewrite the same line using the delegate() keyword as shown below.
Thread T1 = new Thread(delegate() { Number.PrintNumbers(); });
We can also rewrite the same line using the lambda expression as shown below.
Thread T1 = new Thread(() => Number.PrintNumbers());
How to pass the parameter in Thread?
In the constructor of Thread, we can pass the method name which accepts only a single parameter. Then we have to pass the parameter into the Start method.
Why do we need a ParameterizedThreadStart delegate?
When we need to pass data to the thread function then in such situations we need to use the ParameterizedThreadStart delegate. Here is an example that shows the usage of a ParameterizedThreadStart delegate.
namespace MultithreadingDemo { class Program { public static void Main() { Console.WriteLine("Please enter the target number"); object target = Console.ReadLine(); // Create an instance of ParameterizedThreadStart delegate ParameterizedThreadStart parameterizedThreadStart = new ParameterizedThreadStart(Number.PrintNumbers); Thread T1 = new Thread(parameterizedThreadStart); // Pass the traget number to the start function, which // will then be passed automatically to PrintNumbers() function T1.Start(target); } } class Number { public static void PrintNumbers(object target) { int number = 0; if (int.TryParse(target.ToString(), out number)) { for (int i = 1; i <= number; i++) { Console.WriteLine(i); } } } } }
The code in the Main() function can also be written as shown below.
public static void Main() { Console.WriteLine("Please enter the target number"); object target = Console.ReadLine(); Thread T1 = new Thread(Number.PrintNumbers); T1.Start(target); }
In the above example, we are not explicitly creating an instance of the ParameterizedThreadStart delegate. Then how is it working?
It’s working because the compiler implicitly converts new Thread(Number.PrintNumbers) to
new Thread(new ParameterizedThreadStart(Number.PrintNumbers));
When to use ParameterizedThreadStart over ThreadStart delegate?
We need to use the ParameterizedThreadStart delegate if we have some data to pass to the Thread function, otherwise, just use the ThreadStart delegate.
Please note: Using ParameterizedThreadStart delegate and Thread.Start(Object) method to pass data to the Thread function is not type-safe as they operate on object datatype and any type of data can be passed. In the above, If we try to change the data type of the target parameter of the PrintNumbers() function from object to int, a compiler error will be raised as the signature of the PrintNumbers() function does not match with the signature of ParameterizedThreadStart delegate.
How to pass data to the thread function in a type-safe manner?
This MultiThreading Interview Question in C# can be asked in almost all interviews. To pass data to the Thread function in a type-safe manner, encapsulate the thread function and the data it needs in a helper class and use the ThreadStart delegate to execute the thread function. An example is shown below.
namespace ThreadingExample { class Program { public static void Main() { // Prompt the user for the target number Console.WriteLine("Please enter the target number"); // Read from the console and store it in target variable int target = Convert.ToInt32(Console.ReadLine()); // Create an instance of the Number class, passing it // the target number that was read from the console Number number = new Number(target); // Specify the Thread function Thread T1 = new Thread(new ThreadStart(number.PrintNumbers)); // Alternatively we can just use Thread class constructor as shown below // Thread T1 = new Thread(number.PrintNumbers); T1.Start(); } } // Number class also contains the data it needs to print the numbers class Number { int _target; // When an instance is created, the target number needs to be specified public Number(int target) { // The targer number is then stored in the class private variable _target this._target = target; } // Function prints the numbers from 1 to the traget number that the user provided public void PrintNumbers() { for (int i = 1; i <= _target; i++) { Console.WriteLine(i); } } } }
How to retrieve the data from a thread function?
Retrieving data from the Thread function using the callback method.
namespace ThreadStartDelegateExample { // Step 1: Create a callback delegate. The actual callback method // signature should match with the signature of this delegate. public delegate void SumOfNumbersCallback(int sumOfNumbers); // Step 2: Create Number class to compute the sum of numbers and // to call the callback method class Number { // The traget number this class needs to compute the sum of numbers int _target; // Delegate to call when the the Thread function completes // computing the sum of numbers SumOfNumbersCallback _callbackMethod; // Constructor to initialize the target number and the callback delegateinitialize public Number(int target, SumOfNumbersCallback callbackMethod) { this._target = target; this._callbackMethod = callbackMethod; } // This thread function computes the sum of numbers and then invokes // the callback method passing it the sum of numbers public void ComputeSumOfNumbers() { int sum = 0; for (int i = 1; i <= _target; i++) { sum = sum + i; } if (_callbackMethod != null) { _callbackMethod(sum); } } } // Step 3: This class consumes the Number class created in Step 2 class Program { // Callback method that will receive the sum of numbers public static void PrintSumOfNumbers(int sum) { Console.WriteLine("Sum of numbers is " + sum); } public static void Main() { // Prompt the user for the target number Console.WriteLine("Please enter the target number"); // Read from the console and store it in target variable int target = Convert.ToInt32(Console.ReadLine()); // Create an instance of callback delegate and to it's constructor // pass the name of the callback function (PrintSumOfNumbers) SumOfNumbersCallback callbackMethod = newSumOfNumbersCallback(PrintSumOfNumbers); // Create an instance of Number class and to it's constrcutor pass the target // number and the instance of callback delegate Number number = new Number(target, callbackMethod); // Create an instance of Thread class and specify the Thread function to invoke Thread T1 = new Thread(new ThreadStart(number.ComputeSumOfNumbers)); T1.Start(); } } }
What is the difference between Threads and Tasks?
This is one of the most frequently asked Multithreading Interview Questions in C#. Let us understand the differences between them.
- Tasks are the wrapper around Thread and ThreadPool classes. Below are some major differences between Threads and Tasks:
- A Task can return a result but there is no proper way to return a result from Thread.
- We can apply chaining to multiple tasks but we cannot in threads.
- We can wait for Tasks without using Signalling. But in Threads, we have to use event signals like AutoResetEvent and ManualResetEvent.
- We can apply the Parent/Child relationship in Tasks. A Task at one time becomes a parent of multiple tasks. Parent Tasks does not completed until their child’s tasks are completed. We do not have any such mechanism in the Thread class.
- Child Tasks can propagate their exceptions to the parent Task and All child exceptions are available in the AggregateException class.
- Task has an in-build cancellation mechanism using the CancellationToken class.
What is the Significance of Thread.Join and Thread.IsAlive functions in multithreading?
This is one of the most frequently asked Multithreading Interview Questions in C#. Join blocks the current thread and makes it wait until the thread on which the Join method is invoked completes. The join method also has an overload where we can specify the timeout. If we don’t specify the timeout the calling thread waits indefinitely, until the thread on which Join() is invoked completes. This overloaded Join (int millisecondsTimeout) method returns a boolean true if the thread has terminated otherwise false. Join is particularly useful when we need to wait and collect results from a thread execution or if we need to do some cleanup after the thread has been completed.
The IsAlive returns boolean True if the thread is still executing otherwise false.
Program code used in the demo:
namespace MultithreadingDemo { class Program { public static void Main() { Console.WriteLine("Main Thread Started" + Thread.CurrentThread.Name); Thread T1 = new Thread(Program.Thread1Function); T1.Start(); Thread T2 = new Thread(Program.Thread2Function); T2.Start(); //if (T1.Join(1000)) //{ // Console.WriteLine("Thread1Function completed"); //} //else //{ // Console.WriteLine("Thread1Function hot not completed in 1 second"); //} T1.Join(); T2.Join(); Console.WriteLine("Thread2Function completed"); for (int i = 1; i <= 10; i++) { if (T1.IsAlive) { Console.WriteLine("Thread1Function is still doing it's work"); Thread.Sleep(500); } else { Console.WriteLine("Thread1Function Completed"); break; } } Console.WriteLine("Main Thread Completed"); Console.ReadLine(); } public static void Thread1Function() { Console.WriteLine("Thread1Function started"); Thread.Sleep(5000); Console.WriteLine("Thread1Function is about to return"); } public static void Thread2Function() { Console.WriteLine("Thread2Function started"); } } }
What happens if shared resources are not protected from concurrent access in a multithreaded program?
This is one of the most frequently asked Multithreading Interview Questions in C#.NET. The output or behavior of the program can become inconsistent. Let us understand this with an example.
namespace MultithreadingDemo { class Program { static int Total = 0; public static void Main() { AddOneMillion(); AddOneMillion(); AddOneMillion(); Console.WriteLine("Total = " + Total); Console.ReadLine(); } public static void AddOneMillion() { for (int i = 1; i <= 1000000; i++) { Total++; } } } }
Output: Total = 3000000
The above program is a single-threaded program. In the Main() method, AddOneMillion() method is called 3 times, and it updates the Total field correctly as expected, and finally prints the correct total i.e. 3000000.
Now, let’s rewrite the program using multiple threads.
namespace MultithreadingDemo { class Program { static int Total = 0; public static void Main() { Thread thread1 = new Thread(Program.AddOneMillion); Thread thread2 = new Thread(Program.AddOneMillion); Thread thread3 = new Thread(Program.AddOneMillion); thread1.Start(); thread2.Start(); thread3.Start(); thread1.Join(); thread2.Join(); thread3.Join(); Console.WriteLine("Total = " + Total); Console.ReadLine(); } public static void AddOneMillion() { for (int i = 1; i <= 1000000; i++) { Total++; } } } }
Every time we run the above program, we get a different output. The inconsistent output is because the Total field which is a shared resource is not protected from concurrent access by multiple threads. The operator ++ is not thread-safe.
How do protect shared resources from concurrent access?
This Multithreading Interview Question is asked in almost all interviews. So let’s discuss this in detail. There are several ways to protect shared resources from concurrent access. Let’s explore 2 of the options.
Using Interlocked.Increment() method: Modify AddOneMillion() method as shown below. The Interlocked.Increment() Method, increments a specified variable and stores the result, as an atomic operation
public static void AddOneMillion() { for (int i = 1; i <= 1000000; i++) { Interlocked.Increment(ref Total); } }
The other option is to use a lock.
static object _lock = new object(); public static void AddOneMillion() { for (int i = 1; i <= 1000000; i++) { lock (_lock) { Total++; } } }
Which option is better?
From a performance perspective using the Interlocked class is better than using locking. Locking locks out all the other threads except a single thread to read and increment the Total variable. This will ensure that the Total variable is updated safely. The downside is that since all the other threads are locked out, there is a performance hit.
The Interlocked class can be used with addition/subtraction (increment, decrement, add, etc.) on an int or long field. The Interlocked class has methods for incrementing, decrementing, adding, and reading variables atomically.
The following code prints the time taken in ticks. 1 millisecond consists of 10000 ticks.
public static void Main() { Stopwatch stopwatch = Stopwatch.StartNew(); Thread thread1 = new Thread(Program.AddOneMillion); Thread thread2 = new Thread(Program.AddOneMillion); Thread thread3 = new Thread(Program.AddOneMillion); thread1.Start(); thread2.Start(); thread3.Start(); thread1.Join(); thread2.Join(); thread3.Join(); Console.WriteLine("Total = " + Total); stopwatch.Stop(); Console.WriteLine("Time Taken in Ticks = " + stopwatch.ElapsedTicks); }
Please Note: You can use the TimeSpan object to find ticks per second, ticks per millisecond, etc. Stopwatch class is in System.Diagnostics namespace.
What are the Interlocked functions?
Interlocked functions in .NET are useful in multithreading programs to safely change the value of shared variables. By default, C# variables are not thread-safe. When we apply addition, subtraction, or checking the value of variables multiple threads can corrupt the variables. For preventing these dirty reads, we can use Interlocked functions.
Interlocked functions can only work on int, long, double, and float data types
What is a Lock?
The Lock is another synchronization mechanism in C# and one of the famous multi-threading interview questions in .NET. It restricts the critical region so that only one thread can enter a critical region at a time.
The lock needs an object to continue its operation. It applies a lock on a target object and only one thread can lock that target object at a time
What is the Difference between Monitor and lock in C#?
I think you need to be prepared for this Multithreading Interview Questions and Answer if you are preparing to attend the interview questions on Multithreading. So let us discuss this question in detail
- The Lock is just a shortcut for the Monitor statement. Compiler internally converts lock statement to Monitor.Enter and Exit statements.
- Monitor class provides some useful method which is not in the lock statement. These methods are very useful in advanced scenarios.
- The monitor provides the TryEnter method. This method is useful when we need to provide a timeout value.
- TryEnter is also useful when we have to check whether the lock is taken or not. We can pass a boolean parameter that returns true if the lock is taken else returns false.
- The Pulse method notifies a waiting thread of a change in the locked object’s state.
- The Wait method releases the current acquired lock and blocks the current thread until it reacquires the lock.
Both Monitor class and lock provide a mechanism to protect the shared resources in a multithreaded application. The lock is the shortcut for Monitor.Enter with the try and finally.
This means that the following code
static object _lock = new object(); public static void AddOneMillion() { for (int i = 1; i <= 1000000; i++) { lock (_lock) { Total++; } } }
can be rewritten as shown below:
static object _lock = new object(); public static void AddOneMillion() { for (int i = 1; i <= 1000000; i++) { // Acquires the exclusive lock Monitor.Enter(_lock); try { Total++; } finally { // Releases the exclusive lock Monitor.Exit(_lock); } } }
From C# 4, it is implemented slightly differently as shown below
static object _lock = new object(); public static void AddOneMillion() { for (int i = 1; i <= 1000000; i++) { bool lockTaken = false; // Acquires the exclusive lock Monitor.Enter(_lock, ref lockTaken); try { Total++; } finally { // Releases the exclusive lock if (lockTaken) Monitor.Exit(_lock); } } }
So, in short, the lock is a shortcut and it’s the option for basic usage. If we need more control to implement advanced multithreading solutions using TryEnter(), Wait(), Pulse(), & PulseAll() methods, then the Monitor class is our option.
Explain why and how a deadlock can occur in multithreading with an example?
This is one of the most frequently asked Deadlock Interview Questions in C#. Let’s say we have 2 threads Thread 1 and Thread 2 and 2 resources Resource 1 and Resource 2. Thread 1 has already acquired a lock on Resource 1 and wants to acquire a lock on Resource 2. At the same time, Thread 2 has already acquired a lock on Resource 2 and wants to acquire a lock on Resource 1. Two threads never give up their locks, hence a deadlock.
How to resolve a deadlock in a multithreaded program?
There are several techniques to avoid and resolve deadlocks. For example
- Acquiring locks in a specific defined order
- Mutex class
- Monitor.TryEnter() method
What is AutoResetEvent and how it is different from ManualResetEvent?
This is one of the most frequently asked Deadlock Interview Questions in C#. The AutoResetEvent is used when we have to unlock only one single thread from several waiting blocked threads. Below are the differences from ManualResetEvent.
- ManualResetEvent is used for unblocking many threads simultaneously. But AutoResetEvent is used for unblocking only one single thread.
- You have to call Reset() method manually after calling Set() method to reset the ManualResetEvent. But AutoResetEvent Set() method automatically calls the Reset() method.
What is the Semaphore?
This is one of the most frequently asked Deadlock Interview Questions in C#. Semaphores are used when we have to restrict how many threads can enter a critical region. Semaphore is simply an int32 variable maintained by the kernel. We have initialized the Semaphore variable we specify the count of how many threads can enter into the critical region at a time. A thread waiting on a semaphore block when the semaphore is 0 and unblocks when the semaphore is greater than 0.
class Program { static Semaphore semaphore = new Semaphore(5, 5); static void Main(string[] args) { Task.Factory.StartNew(() => { for (int i = 1; i <= 15; ++i) { PrintSomething(i); if (i % 5 == 0) { Thread.Sleep(2000); } } }); Console.ReadLine(); } public static void PrintSomething(int number) { semaphore.WaitOne(); Console.WriteLine(number); semaphore.Release(); } }
When we create instantiate a semaphore object, we have to provide two parameters in the constructor. The first one is the InitialCount and the second one is MaximumCount. MaximumCount denotes the maximum number of threads that can enter concurrently. InitialCount denotes the initial number of threads which can enter the Semaphore directly.
Threads enter the semaphore by calling the WaitOne method and release the semaphore by calling the Release method. You can release multiple threads bypassing the count in the Release method. By default Release method takes one and only releases one thread.
What is Mutex and how it is different from other Synchronization mechanisms?
This is one of the most frequently asked Deadlock Interview Questions in C#. Mutex works similarly to AutoResetEvent and releases only one waiting thread at a time. In the AutoResetEvent any thread can call the Set() method and unblock a waiting thread. But the Mutex object remembers the thread which got the Mutex object and only that thread can release the Mutex.
Mutex object auto record the thread id which got the Mutex object and when a user calls the ReleaseMutex() method for releasing a Mutex object, it internally checks whether the releasing thread is the same as the thread which got the Mutex object if yes, then only it releases the Mutex object else it throws an exception.
Mutex famous example: The mutex is like a key to a toilet. One person can have the key – occupy the toilet – at the time. When finished, the person gives (frees) the key to the next person in the queue.
What is the Race condition?
A race condition happens when two or more threads want to update shared data at the same time.
What is the volatile keyword?
Volatile is used for serializing the access of variables without using the synchronization primitives. You can use volatile with below types:
- References type
- Pointer types
- Values types
- IntPtr
How can you share data between multiple threads?
There are two ways to share data between multiple threads:
- Concurrent collection classes
- Using Synchronization Primitives
What are Concurrent Collection Classes?
.NET Framework class library comes with Concurrent collection classes so that multiple threads can share collection data between them without using synchronization primitives.
There are four types of Concurrent classes.
- ConcurrentQueue
- ConcurrentStack
- ConcurrentDictionary
- ConcurrentBag
What is synchronization and why it is important?
This is one of the most frequently asked Deadlock Interview Questions in C#. We use multiple threads to improve the performance of our application. When multiple threads shares data between there is a chance of data corruption. When one thread is writing to the variable and another thread is reading the same variable at the same time there is a chance of reading corrupt data.
To stop the dirty reads we use synchronization primitives.
Can you count some names of Synchronization primitives?
- Monitor
- Mutex
- Spinlock
- ReaderWriterLock
- Semaphore
- AutoResetEvent
- ManualResetEvent
- Interlocked
- CountDownEvent
- Barrier
What are the four necessary conditions for Deadlock?
- Mutual Exclusion: Resources involved must be unshareable between multiple threads.
- Hold and Wait: Threads hold the resources they have allocated and wait for the resources they want.
- No pre-emption: If the thread locks the resource, other threads cannot take the resource until the thread releases it.
- Circular Wait: A circular chain must exist in which each thread waits for other threads to release resources.
What is LiveLock?
This is one of the most frequently asked Deadlock Interview Questions in C#. A livelock is very similar to a deadlock except for involved threads states are continually changing their state but still, they cannot complete their work.
A real-world example of livelock occurs when two people meet in a narrow corridor, and each tries to be polite by moving aside to let the other pass, but they end up swaying from side to side without making any progress because they both repeatedly move the same way at the same time.
In the next article, I am going to discuss the most frequently asked Exception Handling Interview Questions and Answers in C#. In this article, I try to explain the most frequently asked Multithreading and Deadlock Interview Questions and Answers in C#. I hope you enjoy this Multithreading and Deadlock Interview Questions and Answers in C# article. I would like to have your feedback. Please post your feedback, question, or comments about this Multithreading and Deadlock Interview Questions and Answers article.
Good examples easy to understand..
Very Informative
very informative and covered almost on threading.