← Back to Articles         
Multithreading & Parallel Programming - Part 1

Multithreading & Parallel Programming - Part 1

Published on 12 Oct 2026     6 min read C#
Multithreading and Asynchronous Programming

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.ProcessId returns the current process ID.

  • Environment.CurrentManagedThreadId returns 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/await does not automatically create a new thread.

  • In ASP.NET Core, use async/await for 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:

  • Thread creates a dedicated thread; Join() waits for it to finish.

  • ThreadPool queues 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 Task.Run()

Supports await directly

No

No

Yes

Return result

Requires manual handling

Requires manual handling

Task<T>

Exception handling

Manual

Manual

Through task

Preferred usage

Specialized threading

Low-level background work

Most modern async code

Key Points

  • Thread provides direct control over thread creation.

  • ThreadPool reduces thread creation overhead.

  • Task is a higher-level abstraction.

  • Prefer Task and async/await for 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

lock

Protects critical sections

Interlocked

Atomic operations on individual values

SemaphoreSlim

Limits concurrent access

ConcurrentDictionary

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 lock for operations involving multiple statements.

  • Use Interlocked for 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.

← Back to Articles