14. Async, Threading & Concurrency


Part 1: Core Concepts

The Big Picture

┌─────────────────────────────────────────────────────┐
│ CONCURRENCY                                         │
│ (Doing multiple things in overlapping time periods)│
│                                                     │
│ ┌──────────────────────┐ ┌──────────────────────┐  │
│ │ PARALLELISM          │ │ ASYNCHRONOUS         │  │
│ │ (True simultaneous   │ │ (Non-blocking,       │  │
│ │ execution on         │ │ waiting without      │  │
│ │ multiple cores)      │ │ blocking threads)    │  │
│ │                      │ │                      │  │
│ │ • Parallel class     │ │ • async/await        │  │
│ │ • PLINQ              │ │ • Task (I/O bound)   │  │
│ │ • Task (CPU bound)   │ │ • TAP pattern        │  │
│ └──────────────────────┘ └──────────────────────┘  │
│                                                     │
│ BOTH Built on: Thread & Task                       │
└─────────────────────────────────────────────────────┘

Key Relationships

  • Thread - OS-level execution unit (expensive, limited)

  • Task - High-level abstraction over threads (cheap, efficient)

  • async/await - Language feature to write asynchronous code that looks synchronous

  • Parallel - For CPU-intensive work across multiple cores

  • Concurrency - Umbrella term for all of the above


Part 2: Synchronous vs Asynchronous

Synchronous Execution

What it is: Code executes line-by-line, blocking until each operation completes

Asynchronous Execution

What it is: Code can continue without waiting for operation to complete

Blocking vs Non-Blocking

Blocking:

  • Thread sits idle, waiting

  • Wasteful (threads are expensive resources)

  • Examples: Thread.Sleep(), Task.Wait(), Task.Result

Non-Blocking:

  • Thread is released to do other work

  • Efficient resource usage

  • Examples: await, ContinueWith()


Part 3: CPU-Bound vs I/O-Bound

CPU-Bound Work

What it is: Work that requires computation

Examples:

  • Mathematical calculations

  • Data processing

  • Image/video processing

  • Encryption/decryption

  • Parsing large files

Use:

  • Task.Run()

  • Parallel.For() / Parallel.ForEach()

  • PLINQ

I/O-Bound Work

What it is: Work that waits for external resources

Examples:

  • File reading/writing

  • Network requests (HTTP, database)

  • User input

  • Hardware operations

Use:

  • async/await

  • Asynchronous methods (ReadAsync(), WriteAsync(), etc.)


Part 4: Thread (Legacy Approach)

What it is: Low-level OS thread for executing code concurrently

When to use: ❌ Rarely in modern C# - use Task instead

Namespace: System.Threading

Thread Basics

Thread Properties

Thread Methods

Problems with Threads

Expensive - 1MB stack per thread ❌ Limited - OS has thread limit ❌ Hard to manage - Manual synchronization ❌ No return values - Can't easily get result ❌ No error handling - Exceptions crash thread

Modern Alternative: Use Task instead!


Part 5: Task & Task<T> (Modern Approach)

What it is: High-level abstraction representing an asynchronous operation

When to use: ✅ Default choice for all async/concurrent work

Namespace: System.Threading.Tasks

Task vs Task<T>

Creating Tasks

Task Properties

Waiting for Tasks

Task Continuation

Multiple Tasks

ConfigureAwait

When to use ConfigureAwait(false):

  • ✅ Library code

  • ✅ ASP.NET Core

  • ✅ Background services

  • ❌ UI code (WPF, WinForms)


Part 6: async / await (The Game Changer)

What it is: C# keywords that make asynchronous code look synchronous

When to use: Any I/O-bound operation

Namespace: Built into C# language

Syntax Rules

Key Rules

  1. Methods with await must be marked async

  2. Async methods should return Task or Task<T>

  3. By convention, name async methods with "Async" suffix

  4. Cannot use await in:

    • Synchronous methods

    • lock statements

    • unsafe code

Return Types

Async vs Sync Example

Async All The Way


Part 7: TAP (Task-based Asynchronous Pattern)

What it is: The recommended pattern for async programming in .NET

When to use: All new asynchronous APIs

TAP Rules

  1. Method returns Task or Task<T>

  2. Method name ends with "Async"

  3. Should have an overload accepting CancellationToken

  4. Should be truly asynchronous (not just wrapping sync code)

TAP Example

TAP vs Other Patterns

Pattern
Era
Example

TAP

Modern (.NET 4.5+)

Task\<T\> MethodAsync()

APM

Legacy (.NET 1.0)

BeginMethod() + EndMethod()

EAP

Legacy (.NET 2.0)

MethodAsync() + MethodCompleted event

Always use TAP for new code


Part 8: Parallel Programming (CPU-Bound)

What it is: Execute CPU-intensive work across multiple cores simultaneously

When to use: CPU-bound work that can be divided

Don't use for: I/O-bound work (use async/await instead)

Namespace: System.Threading.Tasks

Parallel Class

Parallel.For

Parallel.ForEach

Parallel.Invoke

ParallelOptions


Part 9: PLINQ (Parallel LINQ)

What it is: Parallel version of LINQ for query processing

When to use: Parallelize LINQ queries on large datasets

Namespace: System.Linq

Basic Usage

PLINQ Options


Part 10: CancellationToken & CancellationTokenSource

What it is: Cooperative cancellation mechanism for async operations

When to use: Allow users to cancel long-running operations

Namespace: System.Threading

CancellationTokenSource

Using CancellationToken

Cancellation Callback


Part 11: Thread Safety & Synchronization

The Problem: Race Conditions

Solution 1: lock (Monitor)

Key Points:

  • Lock on private object, never public

  • Keep locked sections small

  • Never lock on this, typeof(MyClass), or strings

  • Can cause deadlocks if not careful

Solution 2: Interlocked

Interlocked Methods:

Solution 3: Concurrent Collections

Solution 4: SemaphoreSlim

What it is: Limits number of threads accessing a resource

When to use: Rate limiting, connection pooling

Solution 5: ReaderWriterLockSlim

What it is: Allows multiple readers or one writer

When to use: Reads common, writes rare


Part 12: ThreadPool

What it is: Managed pool of worker threads reused for tasks

When to use: Understanding what's under the hood

Direct use: Rarely needed (Task uses this internally)

Namespace: System.Threading


Part 13: Async Coordination Primitives

TaskCompletionSource<T>

What it is: Manually control Task completion

When to use: Wrap callbacks or events into Task

ManualResetEventSlim / AutoResetEvent

What it is: Signal between threads

When to use: Rarely needed with async/await


Part 14: Common Patterns

Pattern 1: Progress Reporting

Pattern 2: Timeout Pattern

Pattern 3: Retry Logic

Pattern 4: Lazy Initialization (Thread-Safe)

Pattern 5: Async Lazy


Part 15: Common Pitfalls

Pitfall 1: Deadlock with .Result or .Wait()

Pitfall 2: Async Void

Pitfall 3: Capturing Loop Variables

Pitfall 4: Starting Too Many Tasks


Part 16: Async Streams (C# 8.0+)

What it is: Asynchronously iterate over data as it arrives

When to use: Large datasets, real-time data, pagination

Namespace: System.Collections.Generic


Part 17: ValueTask<T> (Performance)

What it is: Struct-based Task alternative to avoid allocations

When to use: High-performance scenarios where result is often available synchronously

Caution: More restrictions than Task

Restrictions:

  • Can only await once

  • Cannot use .Result or .Wait()

  • Cannot await simultaneously from multiple threads

If in doubt, use Task<T>


Decision Tree: What to Use When?


Quick Reference Summary

Most Common Scenarios

I/O-Bound (async/await):

CPU-Bound (Parallel):

Thread Safety:

Cancellation:


Guide Complete! You now have a comprehensive Async/Threading reference! ⚡

Last updated