C# Multithreading and Asynchronous Programming
1. What is the difference between a Process and a Thread?
What is it?
-
Process: An independent running application with its own memory space and resources.
-
Thread: A unit of execution within a process. Multiple threads in the same process share memory.
Diagram
Process (ASP.NET Core Application)
│
├── Thread 1 → Handle Request A
├── Thread 2 → Handle Request B
└── Thread 3 → Handle Request C
All threads share process memory.
Example
using System.Diagnostics;
Console.WriteLine($"Process ID: {Environment.ProcessId}");
Console.WriteLine($"Thread ID: {Environment.CurrentManagedThreadId}");
Explanation:
-
Environment.ProcessIdreturns the current process ID. -
Environment.CurrentManagedThreadIdreturns the current managed thread ID.
Key Points
-
A process can contain multiple threads.
-
Processes have separate memory spaces.
-
Threads within the same process share memory.
-
Threads are generally cheaper to create than processes.
-
A process crash is normally isolated from other processes; an unhandled exception on a thread can terminate its entire process.
2. What is the difference between Multithreading and Asynchronous Programming?
What is it?
Multithreading: Uses multiple threads to execute work concurrently, potentially in parallel.
Asynchronous Programming: Allows operations to complete without blocking the calling thread while waiting.
Diagram
Multithreading
--------------
Thread 1 → Task A
Thread 2 → Task B
Thread 3 → Task C
Asynchronous Programming
------------------------
Thread 1 → Start API Call
→ Await (thread released)
→ API Response arrives
Thread 2 → Continue execution
Example
// Multithreading
await Task.Run(() =>
{
Console.WriteLine("Processing on worker thread");
});
// Asynchronous programming
await Task.Delay(2000);
Console.WriteLine("Completed");
Explanation:
-
Task.Run()schedules work on a ThreadPool thread. -
Task.Delay()waits asynchronously without blocking a thread. -
After
await, execution may resume on a different thread.
Key Differences
|
Multithreading |
Asynchronous |
|---|---|
|
Uses multiple threads |
Does not necessarily need multiple threads |
|
Useful for CPU-bound work |
Useful for I/O-bound work |
|
Threads execute work |
Threads can be released during waits |
|
Requires thread synchronization for shared state |
Can also require synchronization |
Key Points
-
Multithreading is about using multiple threads.
-
Asynchronous programming is about non-blocking waits.
-
async/awaitdoes not automatically create a new thread. -
In ASP.NET Core, use
async/awaitfor database calls, HTTP calls, and file I/O.
3. What is the difference between Thread, ThreadPool, and Task?
What is it?
-
Thread: Creates a dedicated operating-system thread.
-
ThreadPool: Maintains reusable worker threads managed by .NET.
-
Task: Represents an asynchronous operation, which may or may not use a thread.
Example
// 1. Thread
Thread thread = new Thread(() =>
{
Console.WriteLine("Thread execution");
});
thread.Start();
thread.Join();
// 2. ThreadPool
ThreadPool.QueueUserWorkItem(_ =>
{
Console.WriteLine("ThreadPool execution");
});
// 3. Task
await Task.Run(() =>
{
Console.WriteLine("Task execution");
});
Explanation:
-
Threadcreates a dedicated thread;Join()waits for it to finish. -
ThreadPoolqueues work for an available worker thread. -
Task.Run()schedules work on the ThreadPool and provides an awaitable result.
The ThreadPool example is fire-and-forget, so its completion is not guaranteed before the program exits.
Key Differences
|
Feature |
Thread |
ThreadPool |
Task |
|---|---|---|---|
|
Dedicated thread |
Yes |
No |
Not necessarily |
|
Thread reuse |
No |
Yes |
Usually, when using |
|
Supports |
No |
No |
Yes |
|
Return result |
Requires manual handling |
Requires manual handling |
|
|
Exception handling |
Manual |
Manual |
Through task |
|
Preferred usage |
Specialized threading |
Low-level background work |
Most modern async code |
Key Points
-
Threadprovides direct control over thread creation. -
ThreadPoolreduces thread creation overhead. -
Taskis a higher-level abstraction. -
Prefer
Taskandasync/awaitfor most modern .NET applications.
4. What is the difference between Concurrency and Parallelism?
What is it?
Concurrency: Multiple tasks make progress during overlapping periods, but not necessarily at the exact same time.
Parallelism: Multiple tasks execute simultaneously on different CPU cores.
Diagram
Concurrency (Single CPU Core)
-----------------------------
Time →
Task A → A A
Task B → B B
Task A → A A
Task B → B B
Tasks take turns.
Parallelism (Multiple CPU Cores)
--------------------------------
Time →
Core 1 → A A A A A
Core 2 → B B B B B
Tasks execute simultaneously.
Example
// Concurrency: asynchronous operations
Task task1 = Task.Delay(2000);
Task task2 = Task.Delay(2000);
await Task.WhenAll(task1, task2);
// Parallelism: CPU-bound operations
Parallel.Invoke(
() => Calculate(1),
() => Calculate(2)
);
static void Calculate(int id)
{
Console.WriteLine($"Task {id}");
}
Explanation:
-
Task.WhenAll()waits for both asynchronous operations to complete. Neither delay occupies a thread while waiting. -
Parallel.Invoke()can execute CPU-bound operations simultaneously using multiple threads.
Key Differences
|
Concurrency |
Parallelism |
|---|---|
|
Tasks overlap in progress |
Tasks execute simultaneously |
|
Can work on a single CPU core |
Requires multiple execution resources |
|
Common in async I/O |
Common in CPU-intensive processing |
|
Improves responsiveness and resource utilization |
Can improve computation speed |
Key Points
-
Concurrency does not guarantee parallel execution.
-
Parallelism is a form of concurrency.
-
Task.WhenAll()does not guarantee multiple threads. -
Parallel.ForEach()is commonly used for CPU-bound parallel processing.
5. What is a Race Condition, and how do you prevent it?
What is it?
A race condition occurs when multiple threads access shared data and the result depends on the timing or order of their execution.
Example: Without Synchronization
int counter = 0;
Parallel.For(0, 1000, i =>
{
counter++;
});
Console.WriteLine(counter);
Expected Output:
1000
Possible Actual Output:
987
Why?
counter++ is not an atomic operation.
Initial counter = 5
Thread 1 → Read counter = 5
Thread 2 → Read counter = 5
Thread 1 → Increment to 6
Thread 2 → Increment to 6
Thread 1 → Write 6
Thread 2 → Write 6
Expected = 7
Actual = 6
One increment is lost.
Solution: Using Interlocked
int counter = 0;
Parallel.For(0, 1000, i =>
{
Interlocked.Increment(ref counter);
});
Console.WriteLine(counter);
Output:
1000
Explanation:
-
Interlocked.Increment()performs an atomic increment. -
Multiple threads cannot interfere with the increment operation.
-
This prevents lost updates.
Ways to Prevent Race Conditions
|
Technique |
Purpose |
|---|---|
|
|
Protects critical sections |
|
|
Atomic operations on individual values |
|
|
Limits concurrent access |
|
|
Thread-safe dictionary operations |
|
Immutable objects |
Avoids mutation of shared state |
Key Points
-
Race conditions occur because of unsynchronized shared access.
-
counter++is not thread-safe. -
Use
lockfor operations involving multiple statements. -
Use
Interlockedfor simple atomic operations. -
Avoid shared mutable state whenever possible.
Quick Interview Revision
|
Concept |
Remember |
|---|---|
|
Process vs Thread |
Process owns resources; threads execute within it |
|
Multithreading vs Async |
Multiple threads vs non-blocking waits |
|
Thread vs ThreadPool vs Task |
Dedicated thread vs reusable threads vs operation abstraction |
|
Concurrency vs Parallelism |
Overlapping progress vs simultaneous execution |
|
Race Condition |
Incorrect results caused by timing-dependent shared access |
Important for ASP.NET Core Web API interviews: Understand why async/await improves scalability for I/O-bound requests, why Task.Run() is generally unnecessary for asynchronous database/API calls, and how to protect shared state in concurrent applications.