Top 10 C# / .NET Multithreading Interview Questions

Question 1. What is the difference between Threads and Tasks?

Tasks are wrapper around Thread and ThreadPool classes. Below are some major differences between Threads and Tasks:

  1. A Task can return a result but there is no proper way to return a result from Thread.
  2. We can apply chaining on multiple tasks but we can not in threads.
  3. We can wait on Tasks without using Signalling. But in Threads we have to use event signals like AutoResetEvent and ManualResetEvent.
  4. We can apply Parent/Child relationship in Tasks. A Task at one time becomes parent of multiple tasks. Parent Task does not complete until it’s child tasks are completed. We do not have any such mechanim in Thread class.
  5. Child Tasks can propagate their exceptions to to parent Task and All child exceptions are available in AggregateException class.
  6. Tasks has in-build cancellation mechanism using CancellationToken class.

Question 2. 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. Below are the some Interlocked functions.

1) Add(ref int location1, int value) : For safely adding the value into int variable.

2) Increment(ref int location1) : For safely increase the value of int variable by 1.

3) Decrement(ref int location1) : For safely decrease the value of int variable by 1.

4) Exchange(ref int location1, int value) : For safely set the new value into int value location1.

5) CompareExchange(ref int location1, int value, int comparand) : It first check the old value in location1 to comparand value. It both are equal then only it set the new value into int value location1.

Question 3. What is AutoResetEvent and how it is different from ManualResetEvent?

AutoResetEvent is used when we have to unlock only one single thread from several waiting blocked threads.

Below are differences from ManualResetEvent.

  1. ManualResetEvent is used for unblocks many threads simultaneously. But AutoResetEvent is used for unblocks only one single thread.
  2. You have to call Reset() method manually after calling Set() method to reset the ManualResetEvent. But AutoResetEvent Set() method automatically calls the Reset() method.

Question 4. What is Semaphore?

Semaphores are used when we have to restrict how many threads can enter a critical region. Semaphore is simply a int32 variable maintained by the kernel. We have initialize the Semaphore variable we specify the count how many threads can enter into critical region at a time. A thread waiting on a semaphore blocks when the semaphore is 0 and unblocks when the semaphore is greater than 0.

Below is the sample program using Semaphore.

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. First one is the InitialCount and 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 by passing the count in the Release method. By default Release method takes one and only release one thread.

Question 5. What is Mutex and how it is different from another Synchronization mechanisms?

Mutex works similarly like 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 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 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:

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.

Question 6. What is Lock?

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.

Locks needs an object to continue its operation. It apply a lock on a target object and only one thread can lock that target object at a time. Below is the example of lock object.

static object obj = new object();

public static void DoWork()
{
    lock(obj)
    {
        Console.WriteLine("Multi threading Interview questions in C#");
    }
}

Question 7. What is ReaderWriterLockSlim?

ReaderWriterLock is useful in multithreaded programs where mainly threads needs only read access and occasionally some thread want to update the shared data. Reader threads takes the reader lock on shared data by calling EnterReaderLock method of ReaderWriterLockSlim. In the reader lock no other threads can update the shared data. Thread exits from critical region by calling ExitReadLock method. 

When any thread want to update the data, it call the EnterWriterLock method. After the EnterWriterLock only one thread can modify the data and no other threads can not read the data.

Below is the example of ReaderWriterLockSlim class.

public class Transaction : IDisposable
{
    private readonly ReaderWriterLockSlim myLock = new ReaderWriterLockSlim();
    private int count;

    public void PerformTransaction()
    {
        myLock.EnterWriteLock();
        count++;
        myLock.ExitWriteLock();
    }
    public int LastTransaction
    {
        get
        {
            myLock.EnterReadLock();
            int temp = count;
            myLock.ExitReadLock();
            return temp;
        }
    }
    public void Dispose() { myLock.Dispose(); }
}

Question 8. What are Concurrent Collection Classes?

.NET Framework Class Library (FCL) provides four thread-safe collection classes in the System.Collections.Concurrent namespace.

  1. ConcurrentQueue : It provides a thread-safe First-In-First-Out (FIFO) data structure. You can read more about ConcurrentQueue here.
  2. ConcurrentStack : It provides a thread-safe Last-In-First-Out (LIFO) data structure. You can read more about ConcurrentStack here.
  3. ConcurrentDictionary : It provides a thread-safe collection class to store key-value pairs. You can read more about ConcurrentDictionary here.
  4. ConcurrentBag : It provides a thread-safe collection class to store objects in unordered way. You can read more about ConcurrentBag here.

Question 9. How can you make a Singleton class thread safe?

To make singleton class thread-safe, we can apply double-check locking algorithm. In this algorithm, we can put two if conditions to check object is null or not. 

First if condition checks if the object is not null then return the already created object. Second condition we put inside the lock to check if object is null then only create a new object. The benefit of putting inside condition is to check, if multiple threads bypass the first condition at the same time and waiting for their turn to take the lock. Then in this case both threads will create a new object. For stopping the above issue, we have to put the second condition.

Below is the example of double-checking locking.

public class Singleton
    {
        private static readonly Object s_lock = new Object();

        private static Singleton singleton = null;

        private Singleton()
        {
        }
        
        public static Singleton GetSingleton()
        {
            if (singleton != null) return singleton;

            Monitor.Enter(s_lock);
            if (singleton == null)
            {
                Singleton temp = new Singleton();
            }
            Monitor.Exit(s_lock);
            return singleton;
        }
    }

Question 10. What is the Difference between Lock and Monitor?

Lock is just a shortcut for Monitor statement. Compiler internally convert lock statement to Monitor.Enter and Exit statements.

Monitor class provides some useful method which are not in lock statement. These methods are very useful in advanced scenarios.

  1. Monitor provides TryEnter method. This method is useful when we need to provide timeout value.
  2. TryEnter is also useful when we have to check whether lock is taken or not. We can pass a boolean parameter which returns true if lock is taken else returns false.
  3. Pulse method notifies a waiting thread of a change in the locked object’s state.
  4. Wait method release the current acquired lock and block the current thread until it reacquires the lock.

These are top 10 interview questions. Do you know any question which have asked every time in your interview? Please let me know in the comments section.