Tech Point Fundamentals

Sunday, August 21, 2022

C# Interview Questions and Answers - Part 14

C# Interview Questions and Answers - Part 14

csharp-interview-questions-and-answers-part1

Are you preparing for the C# Interview? If yes, then you are at the right place. This is the C# Interview Questions and Answers article series. Here we will see the Top 150+ C# Interview Questions with Answers. 

Please visit our YouTube Channel for Interviews and other videos by below link:



Please read the complete Design Patterns Interview Questions and Answers series here.


Introduction


This is the 14th part of this C# Interview Questions and Answers article series. Each part contains 10 C# Interview Questions with Answers. Please read all the C# Interview Questions list here.

I will highly recommend you to please read the previous parts over here before continuing the current part:





C# Interview Questions and Answers - Part 14


Q141. What is the difference between Mutex and Monitor?

Both mutex and monitor are used for thread synchronizations in C#. 

A critical section is a piece of code that accesses a shared resource (data structure or device) but the condition is that only one thread can enter in this section at a time. When two or more threads need to access a shared resource at the same time, the system needs a synchronization mechanism to ensure that only one thread at a time uses the resource.




Mutex:

Mutex stands for Mutual Exclusion. Mutex also works as a lock i.e. acquired an exclusive lock on a shared resource from concurrent access, but it works across multiple processes. 

An exclusive locking is basically used to ensure that at any given point of time, only one thread can enter into the critical section. A "Mutex" is a mechanism that acts as a flag to prevent two threads from performing one or more actions simultaneously. 

Mutex is a synchronization primitive that grants exclusive access to the shared resource to only one thread. If a thread acquires a Mutex, the second thread that wants to acquire that Mutex is suspended until the first thread releases the Mutex.

The Mutex class provides the WaitOne() method which we need to call to lock the resource and similarly it provides ReleaseMutex() which is used to unlock the resource. Note that a Mutex can only be released from the same thread which obtained it.



Monitor:

The monitor is also used to provide thread safety to a shared resource in a multithreaded application same as the lock. A monitor is a mechanism for ensuring that only one thread at a time may be running a certain piece of code i.e critical section.

The Monitor class provides a mechanism that synchronizes access to objects. This can be done by acquiring an exclusive lock on the object so that only one thread can enter into the critical section at any given point of time.

A monitor has a lock, and only one thread at a time may acquire it. To run in certain blocks of code (critical section), a thread must have acquired the monitor. A monitor is always associated with a specific object and cannot be dissociated from or replaced within that object.



In C# monitor is a static class and belongs to the System.Threading namespace. As a static class, it provides a collection of static methods like Enter, TryEnter, Wait, Pulse, PulseAll, and Exit.

Enter and TryEnter: These two methods are used to acquire an exclusive lock for an object. This action marks the beginning of a critical section. No other thread can enter into the critical section unless it is executing the instructions in the critical section using a different locked object.

Wait: The Wait method is used to release the lock on an object and permit other threads to lock and access the object by blocking the current thread until it reacquires the lock. The calling thread waits while another thread accesses the object. Pulse signals are used to notify waiting threads about changes to an object’s state.

Pulse (signal), PulseAll: The above two methods are used to send a signal to one or more waiting threads. The signal notifies a waiting thread that the state of the locked object has changed, and the owner of the lock is ready to release the lock.

Exit(): The Exit method is used to release the exclusive lock from the specified object. This action marks the end of a critical section protected by the locked object.

A lock is a shortcut for Monitor.Enter() with try and finally. So, the lock provides the basic functionality to acquire an exclusive lock on a synchronized object. But, If you want more control to implement advanced multithreading solutions using TryEnter() Wait(), Pulse(), and PulseAll() methods, then the Monitor class is your option.



Q142. What is Semaphore C#? How does the Semaphore work?

The semaphore works similar to the Monitor and Mutex but the semaphore lets you set a limit on how many threads have access to a critical section. So, in real-time, we need to use Semaphore when we have a limited number of resources and we want to limit the number of threads that can use it.

The Semaphores are Int32 variables that are stored in operating system resources. When we initialize the semaphore object we initialize it with a number. This number is basically used to limit the threads that can enter into the critical section. 

So, when a thread enters into the critical section, it decreases the value of the Int32 variable by 1 and when a thread exits from the critical section, it then increases the value of the Int32 variable by 1. The most important point that you need to remember is when the value of the Int32 variable is 0, then no thread can enter into the critical section.



System.Threading.Semaphore provides all the methods and properties which are required to implement Semaphore. To use a semaphore in C#, you first need to instantiate an instance of a Semaphore object. The constructor, at a minimum, takes two parameters. The first is the number of resource slots initially available when the object is instantiated. The second parameter is the maximum number of slots available. 

static Semaphore sem = new Semaphore(no-of-resource, max-slot-size);

After you instantiated your Semaphore object, you simply need to call the WaitOne() method when entering an area of code that you want to be restricted to a certain number of threads. 

When processing finishes, call the Release() method to release the slot back to the pool. The count on a semaphore is decremented each time a thread enters the semaphore and incremented when a thread releases the semaphore. 

When the count is zero, subsequent requests block until other threads release the semaphore. When all threads have released the semaphore, the count is at the maximum value specified when the semaphore was created.



Q143. What is deadlock? How can you prevent deadlock in C#?

A deadlock happens when two or more threads or processes need some resource to complete their execution that is held by the other process. The deadlock can happen in Operating Systems, SQL Transactions, Multi-threaded applications, etc.

A deadlock is not a crash. Unlike a crash, an application hang may not produce a crash dump or trigger custom failure logic. A deadlock occurs when a set of concurrent workers are waiting on each other to make forward progress before any of them can make forward progress.

In C# a deadlock is a situation where two or more threads are frozen in their execution because they are waiting for each other to finish. 

For example, if we have two threads Thread1 and Thread2, and we have two resources Resource1 and Resource2. The Thread1 locked the Resource1 and trying to acquire a lock on Resource2. At the same time, Thread2 acquired a lock on Resource2 and trying to acquire a lock on Resource1. 



Four Necessary Conditions of Deadlock:

A deadlock occurs only if the below four Coffman conditions hold true. But these conditions are not mutually exclusive. So all four conditions are necessary for the deadlock to occur.  If anyone is prevented or resolved, the deadlock is resolved.

1. Mutual Exclusion: There is always a limited number of a particular resource. A resource can only be held by one process at a time. When one thread owns some resource, another cannot acquire it.

2. Hold and Wait: This is the ability of a thread or process to hold one resource and request another resource at the same time. 

3. No Preemption: One thread can't force another thread to release a lock. This means a resource cannot be preempted from a process by force.  A process can only release a resource voluntarily. 

4. Circular Wait: There is a cycle of threads, each of which is waiting for the next to release a resource before it can continue. A process is waiting for the resource held by the second process, which is waiting for the resource held by the third process, and so on, till the last process is waiting for a resource held by the first process. This forms a circular chain.



Deadlock Preventions:

To prevent deadlock, somehow we have to break any one of the above four necessary conditions of deadlock. One simplest way is to force the thread to release the resource or lock after a specified time. So we can use Monitor.TryEnter method which takes a parameter for a timeout in milliseconds. Using that parameter we can specify a timeout for the thread to release the lock. 

if (Monitor.TryEnter(Account, 3000))
{
Console.WriteLine($"{Thread.CurrentThread.Name} acquired lock on {Account.ID}");
}



Q144. What do you mean by thread-safe? How can you share data between multiple threads?

When multiple threads can make calls to the properties and methods of a single object, it is critical that those calls be synchronized. Otherwise, one thread might interrupt what another thread is doing, and the object could be left in an invalid state. A class whose members are protected from such interruptions is called thread-safe.

The problems with sharing data between threads are all due to the consequences of modifying data. If all shared data is read-only, there’s no problem, because the data read by one thread is unaffected by whether or not another thread is reading the same data. 

However, if data is shared between threads, and one or more threads start modifying the data, there’s a lot of potential for trouble. We don’t want a situation when two threads access the same data and update it in the wrong way.

The .NET provides several strategies to synchronize access to the instance and static members like  Monitor class, SynchronizationAttribute, and System.Collections.Concurrent namespace etc.

Please watch the volatile keyword video here for more details.








Q145. How can you retrieve data from a thread? What is a callback method?

We know that both ThreadStart and ParameterizedThreadStart delegates have void return types and only these can be used to create a delegate. So, using the above two delegates we cannot return any data back from a thread. 

So what if we want some result or data back from a delegate after processing is completed?  We can retrieve data from a thread function using a callback method. 

We can define a callback function or method as a function pointer that is being passed as an argument to another function. And then it is expected to call back that function at some point in time.



Q146. What is Anonymous Method in C#?

An anonymous method is a method without a name. The Anonymous methods can be defined using the keyword delegate and can be assigned to a variable of the delegate type.

Fundamental Points of Anonymous Method:

  1. An anonymous method can be defined using the delegate keyword.
  2. The anonymous method must be assigned to a delegate type.
  3. The anonymous method can access outer variables or functions as well.
  4. An anonymous method can also be passed as a parameter.
  5. The anonymous method can be used as an event handler.
  6. An anonymous method in C# cannot contain any jump statement like goto, break or continue.
  7. The anonymous method cannot access the REF or OUT parameter of an outer method.
  8. The Anonymous methods cannot have or access the unsafe code.








Without an anonymous method, we have to create a method and then assign it to the delegate. A delegate can be associated with a named method. When you instantiate a delegate by using a named method, the method is passed as a parameter:

public delegate void PrintDelegate(string name);
PrintDelegate del = new PrintDelegate(funPrintName);
del.Invoke("TechPointFundamentals");

void funPrintName(string name)
{
Console.Writeline(Hello + " " + name);
}

Since an anonymous method is also related to a delegate, without binding the method name explicitly to a delegate, we can bind a code block (anonymous method) directly to a delegate.



Named methods are the only way to instantiate a delegate in earlier versions of C#. However, in a situation where creating a new method is an unwanted overhead, C# enables you to instantiate a delegate and immediately specify a code block that the delegate will process when it is called. The block can contain either a lambda expression or an anonymous method.

PrintDelegate del = delegate(string name) 
{
Console.Writeline(Hellow + " " + name);
};
del.Invoke("TechPointFundamentals");

Generally, anonymous methods are suggested when the code volume is very less. The anonymous method is used for lesser writing. We can directly write the method code block and pass that within the delegate parameter.



Q147. What is Lambda Expression in C#?

The Lambda Expression in C# is the shorthand for writing the anonymous function. A lambda expression simplifies the anonymous function. You use a lambda expression to create an anonymous function.

Use the lambda declaration operator => to separate the lambda's parameter list from its body. To create a lambda expression, you specify input parameters (if any) on the left side of the lambda operator and an expression or a statement block on the other side.

We know that the delegate keyword is used for creating anonymous methods. 

PrintDelegate del = delegate(string name) 
{
Console.Writeline(Hellow + " " + name);
};

We can simplify the above anonymous method using a lambda expression:

PrintDelegate del = (name) =>
{
Console.Writeline(Hellow + " " + name);
};



You enclose the input parameters of a lambda expression in parentheses. Specify zero input parameters with empty parentheses:

Action line = () => Console.WriteLine();

If a lambda expression has only one input parameter, parentheses are optional:

Func<int, int> cube = x => x * x * x;

However, if there are multiple parameters, all the input parameters are separated by commas:

Func<int, int, bool> testForEquality = (x, y) => x == y;

Sometimes the compiler can't infer the types of input parameters. You can specify the types explicitly but all the input parameter types must be all explicit or all implicit; otherwise, a compiler error occurs.

Func<int, string, bool> isTooLong = (int x, string s) => s.Length > x;



Types of Lambda Expression:

A lambda expression can be of any of the following two forms:

1.  Expression Lambda:

A lambda expression with an expression on the right side of the => operator is called an expression lambda. Expression lambda has an expression as its body.

An expression lambda returns the result of the expression and takes the following basic form: 

(input-parameters) => expression

The body of an expression lambda can consist of a method call as well. 



2. Statement Lambda:

A statement lambda resembles an expression lambda except that its statements are enclosed in braces. Statement lambda has a statement block as its body:  

(input-parameters) => { <sequence-of-statements> }

The body of a statement lambda can consist of any number of statements; however, in practice, there are typically no more than two or three.

Any lambda expression can be converted to a delegate type. The delegate type to which a lambda expression can be converted is defined by the types of its parameters and return value. If a lambda expression doesn't return a value, it can be converted to one of the Action delegate types; otherwise, it can be converted to one of the Func delegate types.



Application of Lambda Expression:

  • You can use lambda expressions in any code that requires instances of delegate types or expression trees. 
  • You can also use lambda expressions when you write LINQ in C#.
  • You can easily create lambda expressions and statements that incorporate asynchronous processing by using the async and await keywords.  
  • Starting with C# 7.0, the C# language provides built-in support for tuples. You can provide a tuple as an argument to a lambda expression, and your lambda expression can also return a tuple. 



Q148. What is a thread? What is the thread life cycle or different states of a thread?

When a process starts, the CLR  automatically creates a single foreground thread to execute the application code. Along with this main foreground thread, a process can create one or more threads to execute a portion of the program code associated with the process. These threads can execute either in the foreground or in the background.

C# threading allows developers to create multiple threads in C#. A C# program is single-threaded by design. That means, only one path of the code is executed at a time by the main or primary thread. The entry point of a C# program starts in the Main method and that is the path of the primary thread.

However, sometimes we need multiple child threads to accomplish a task. For that C# allows us to create a thread from the main thread. The Thread class represents a thread and provides functionality to create and manage a thread’s lifecycle, and its properties, such as status, priority, state. The Thread class is defined in the System.Threading namespace so it must be imported before you can use any threading-related types.

Thread ChildThread = new Thread(new ThreadStart(Program.MultiThreadTask));



Types of Thread:

There are two types of threads, foreground, and background. Apart from the main application thread, all threads created by calling a Thread class constructor are foreground threads. 

Background threads are the threads that are created and used from the ThreadPool, which is a pool of worker threads maintained by the runtime.




Different Thread States:

During its lifecycle, each thread goes through a state. A thread in C# at any point of time exists in any one of the following states.

Unstarted State: When a thread is created, it is always in an unstarted state by default. When an instance of a Thread class is created, it is in an unstarted state. 
 
Runnable State:  A thread that is ready to run is moved to a runnable state. In this state, a thread might actually be running or it might be ready to run at any instant of time. When another thread calls the Thread.Start method on the new thread, and the call returns. It is the responsibility of the thread scheduler to give the thread, time to run.

Running State:  A thread that is running. In this state, the thread gets the processor time to execute.

Not Runnable State or WaitSleepJoin: A thread that is not executable because of:
  • The sleep () method is called.
  • Wait() method is called.
  • Due to the I/O request.
  • Suspend() method is called.

Dead State: When the thread completes its task, then the thread enters into dead, terminates, or abort state.




Methods to Manage Threads:

  1. The Sleep() method is used to temporarily suspend the current execution of the thread for specified milliseconds, so that other threads can get the chance to start the execution, or may get the CPU for execution.
  2. The Join() method is used to make all the calling threads to wait until the main thread, i.e. joined thread completes its work.
  3. The Abort () method is used to abort the thread.
  4. The Suspend() method is called to suspend the thread.
  5. The Resume () method is called to resume the suspended thread.
  6. The Start() method is used to send a thread into a runnable state.



Q149. What is the difference between task.wait(), task.delay(), and thread.sleep() in C#?

Task.Wait():

If you want to make the main thread execution wait until the child tasks are completed, then you need to use the Wait method of the Task class. The Wait() method of the Task class will block the execution of current main threads until the assigned task has completed its execution. It is a blocking Synchronization method that allows the calling thread to wait until the current task has been completed.  

You can synchronize the execution of the calling thread and the asynchronous tasks it launches by calling a Wait method to wait for one or more tasks to complete. To wait for a single task to complete, you can call its Task.Wait() method. A call to the Wait method blocks the calling thread until the single class instance has completed execution. 

Tasks are used for asynchronous programming. A long-running task is performed using the asynchronous call and not blocking the current thread. But if we want to get the result of a task before continuing to the next task, we must have to wait till the previous task gets completed. So to wait for a single task we can use the Wait() method of the Task object.

Task output = Task.Factory.StartNew(LongRunningOperation); 
output.Wait(); 



However, there is one more implicit way to wait for a single task i.e to check for the result. But in this case, we should use the generic task. The LongRunningOperation method should call a method that has a return type. 

Task output = Task.Factory.StartNew(LongRunningOperation);
Console.WriteLine(output.Result);

If there are multiple threads using the task and you have to wait for all of them to complete their job then you must have to use the WaitAll method of task.

Task outputA = Task.Factory.StartNew(LongRunningOperationA);
Task outputB = Task.Factory.StartNew(LongRunningOperationB);
Task.WaitAll(outputA, outputB);

However, you can also use the WhenAll method of the task method as well for the same:

var allTask = Task.WhenAll(outputA, outputB);
if (allTask.IsCompleted)
{
foreach (var item in allTask.Result)
{
Console.Write(string.Format("result {1}", item));
}
}

If you want to wait for only any single task to complete. You have to use the WaitAny method of the Task class. The parameter of the WaitAny method is the same task collection.



Task.Delay():

We use Task.Delay() when we want to wait for a specific amount of time in an asynchronous fashion. When we need to wait for a specific amount of time and try something again then we use the delay method. So Task.Delay() is an asynchronous delay.

The Task.Delay() method creates a task that will complete after a time delay. There is 4 overloaded version for the delay and all use a time span to wait before completing the returned task. 

The Task.Delay() method produces a Task object that finishes after the specified time. However, you can use it along with the Wait()  method as well.

Task.Delay(1000);
Task.Delay(1000).Wait();




Thread.Sleep():

Thread.Sleep() is a synchronization delay. It suspends the current thread for the specified number of milliseconds or timestamps. It pauses the execution of a program for a specified time. It does not utilize the CPU during the pause time. It is useful for waiting on an external Task.

In C#, the sleep method is useful to suspend or pause the current thread execution for a specified time. We can suspend the thread execution either by passing the time in milliseconds or with the TimeSpan property.

Thread.Sleep(1000); // Time in milliseconds
Thread.Sleep(new TimeSpan(0,0,1)); // Time in hours, minutes, seconds



Thread.Sleep vs Task.Delay:

  1. Thread.Sleep() is a synchronization delay while Task.Delay() is an asynchronous delay.
  2. Thread.Sleep() blocking threads but Task.Delay() does not block the current thread.
  3. Thread.Sleep() can not be canceled but Task.Delay() can be canceled.
  4. Task.Delay() essentially creates a task that runs for a given time but Thread.Sleep() hibernate the current thread for a given time.

If you decompile the Task.Delay(), it is nothing but a timer wrapped in a task. The main difference between Task.Delay() and Thread.Sleep() is that Task.Delay() designed to run asynchronously. 

So using Task.Delay() in synchronous code doesn't make any sense. Similarly using Thread.Sleep() in asynchronous code does not make any sense. Usually the await Keyword call Task.Delay().




Q150. What is string interpolation in C#?

A format string is a string whose contents are determined dynamically at run time. Format strings are created by embedding interpolated expressions or placeholders inside of braces within a string. Everything inside the braces ({...}) will be resolved to a value and output as a formatted string at run time. There are two methods to create format strings: string interpolation and composite formatting.

The String.Format() converts the value of objects to strings based on the formats specified and inserts them into another string. The String.Format() utilizes placeholders in braces to create a format string. String.Format() method has 8 overloaded formats to provide options to format various objects and variables that allow various variables to format strings.
Index position is started with zero in the String.Format() method. 

Console.WriteLine( String.Format("Total Count : {0}",100));



The String Interpolation feature is available in C# 6.0 and later, interpolated strings are identified by the $ special character and include interpolated expressions in braces. 

Use string interpolation to improve the readability and maintainability of your code. String interpolation achieves the same results as the String.Format() method, but improves ease of use and inline clarity.

Console.WriteLine( $"Total Count : {obj.Count}");

Beginning with C# 10, you can use string interpolation to initialize a constant string when all the expressions used for placeholders are also constant strings.



To Be Continued Part-15...


Recommended Articles






Thanks for visiting this page. Please follow and join us on LinkedInFacebookTelegramQuoraYouTubeTwitterPinterestTumbler, and VK  for regular updates.

   

No comments:

Post a Comment

Please do not enter any HTML. JavaScript or spam link in the comment box.