Multi-Threading Interview Questions and Answers in C#

Multi-Threading Interview Questions and Answers in C#

In this article, I am going to discuss the most frequently asked Multi-Threading Interview Questions and Answers in C#. Please read our previous article where we discussed the most frequently asked Nested Types Interview Questions in C# with Answers. As part of this article, we are going to discuss the following Multi-Threading Interview Questions and Answers in C#.

  1. What are the Thread and Process?
  2. What is the difference between Process and Thread?
  3. Why we need Multi-threading in our project?
  4. What are the advantages and disadvantages of multithreading in C#?
  5. How can we create a Thread in C#?
  6. Why does a delegate need to be passed as a parameter to the Thread class constructor?
  7. How to pass the parameter in Thread?
  8. Why we need a ParameterizedThreadStart delegate?
  9. When to use ParameterizedThreadStart over ThreadStart delegate?
  10. How to pass data to the thread function in a type-safe manner?
  11. How to retrieve the data from a thread function?
  12. What is the difference between Threads and Tasks?
  13. What is the Significance of Thread.Join and Thread.IsAlive functions in multithreading?
  14. What happens if shared resources are not protected from concurrent access in a multithreaded program?
  15. How to protect shared resources from concurrent access?
  16. What are Interlocked functions?
  17. What is Lock?
  18. What is the Difference between Monitor and lock in C#?
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?

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 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 we need Multi-threading in our project?

This is one of the most frequently asked Multi-Threading Interview Questions in C#.NET. Let us discuss this question

Multi-threading is used to run multiple threads simultaneously. Some main advantages are:

  1. 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.
  2. Threads are much lightweight than process. They don’t get their own resources. They used the resources allocated to a process.
  3. Context-switch between threads takes less time than process.
What are the advantages and disadvantages of multithreading?

I think this Multi-Threading Interview Question is the most asked interview question in the dot net. So let us discuss the advantages and disadvantages

Advantages of multithreading:

  1. To maintain a responsive user interface
  2. It makes the efficient use of processor time while waiting for I/O operations to complete.
  3. To split large, CPU-bound tasks to be processed simultaneously on a machine that has multiple CPUs/cores. 

Disadvantages of multithreading:

  1. On a single-core/processor machine threading can affect performance negatively as there is overhead involved with context-switching.
  2. Have to write more lines of code to accomplish the same task.
  3. 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.

Multi-Threading Interview Questions and Answers in C#

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 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 we need a ParameterizedThreadStart delegate?

When we need to pass data to the thread function then in such situations we need to use 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 ParameterizedThreadStart delegate if we have some data to pass to the Thread function, otherwise, just use 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 PrintNumbers() function from object to int, a compiler error will be raised as the signature of 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 Multi-Threading Interview Questions 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 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?
  1. Tasks are the wrapper around Thread and ThreadPool classes. Below are some major differences between Threads and Tasks:
  2. A Task can return a result but there is no proper way to return a result from Thread.
  3. We can apply chaining to multiple tasks but we cannot in threads.
  4. We can wait for Tasks without using Signalling. But in Threads, we have to use event signals like AutoResetEvent and ManualResetEvent.
  5. We can apply the Parent/Child relationship in Tasks. A Task at one time becomes a parent of multiple tasks. Parent Task does not complete until it’s child tasks are completed. We do not have any such mechanism in the Thread class.
  6. Child Tasks can propagate their exceptions to the parent Task and All child exceptions are available in the AggregateException class.
  7. Task has an in-build cancellation mechanism using the CancellationToken class.
What is the Significance of Thread.Join and Thread.IsAlive functions in multithreading?

Join blocks the current thread and makes it wait until the thread on which Join method is invoked completes. 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 boolean true if the thread has terminated otherwise false. Join is particularly useful when we need to wait and collect result from a thread execution or if we need to do some cleanup after the thread has 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 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 to protect shared resources from concurrent access?

This Multithreading Interview Questions asked in almost all interviews. So let discuss this in details. 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 Interlocked class is better over 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 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 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.

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 this Multithreading Interview Questions and Answer if you are preparing to attend the interview questions on Multithreading. So let us discuss this question in details

  1. The Lock is just a shortcut for Monitor statement. Compiler internally converts lock statement to Monitor.Enter and Exit statements.
  2. Monitor class provides some useful method which is not in lock statement. These methods are very useful in advanced scenarios.
  3. The monitor provides TryEnter method. This method is useful when we need to provide a timeout value.
  4. TryEnter is also useful when we have to check whether the lock is taken or not. We can pass a boolean parameter which returns true if the lock is taken else returns false.
  5. The Pulse method notifies a waiting thread of a change in the locked object’s state.
  6. Wait method releases the current acquired lock and blocks the current thread until it reacquires the lock.

Both Monitor class and lock provides 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 the 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. 

In the next article, I am going to discuss the most frequently asked Deadlock Interview Questions and Answers in C#. In this article, I try to explain most frequently asked Multi-Threading Interview Questions and Answers in C#. I hope you enjoy this Multi-Threading Interview Questions and Answers in C# article. I would like to have your feedback. Please post your feedback, question, or comments about this article.

1 thought on “Multi-Threading Interview Questions and Answers in C#”

Leave a Reply

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