C#  

Synchronous vs Asynchronous Programming with CPU-bound & I/O-bound Examples

1️⃣ Introduction

When writing software, especially in .NET or any modern language, you’ll often hear the terms synchronous and asynchronous.

Understanding these concepts — along with CPU-bound and I/O-bound work — will help you write faster, more responsive applications.

2️⃣ Synchronous Programming

Definition

In synchronous programming, tasks are executed one after another.

The next task won’t start until the previous one is completely finished.

Real-life analogy

Imagine you are at a restaurant.

The waiter takes your order, goes to the kitchen, waits until your food is ready, serves you, and only then takes the next customer’s order.

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Console.WriteLine("Task 1 start");
        Task1(); // Wait until complete
        Console.WriteLine("Task 2 start");
        Task2(); // Wait until complete
        Console.WriteLine("All done");
    }

    static void Task1()
    {
        Thread.Sleep(3000); // Simulate 3 sec work
        Console.WriteLine("Task 1 complete");
    }

    static void Task2()
    {
        Thread.Sleep(2000); // Simulate 2 sec work
        Console.WriteLine("Task 2 complete");
    }
}

Total time: 5 seconds (3s + 2s)

3️⃣ Asynchronous Programming

Definition

In asynchronous programming, you can start multiple tasks and let them run independently.

The program doesn’t wait for a task to finish before moving on — unless explicitly told to.

Real-life analogy

In the same restaurant, the waiter takes your order and sends it to the kitchen, then takes orders from other customers while your food is cooking.

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Console.WriteLine("Task 1 start");
        var task1 = Task1(); // Start without waiting
        Console.WriteLine("Task 2 start");
        var task2 = Task2(); // Start without waiting

        await Task.WhenAll(task1, task2); // Wait for both to finish
        Console.WriteLine("All done");
    }

    static async Task Task1()
    {
        await Task.Delay(3000); // Simulate 3 sec work
        Console.WriteLine("Task 1 complete");
    }

    static async Task Task2()
    {
        await Task.Delay(2000); // Simulate 2 sec work
        Console.WriteLine("Task 2 complete");
    }
}

Total time: ~3 seconds (runs in parallel)

4️⃣ CPU-bound vs I/O-bound

CPU-bound

  • Definition: CPU is fully busy doing calculations (no waiting).
  • Examples: Image processing, data encryption, prime number calculation.
  • Best optimization: Multi-threading or parallel processing.

C# CPU-bound Example

static void CalculatePrimeNumbers()
{
    for (int i = 2; i < 500000; i++)
    {
        bool isPrime = true;
        for (int j = 2; j * j <= i; j++)
        {
            if (i % j == 0) { isPrime = false; break; }
        }
    }
}

I/O-bound

  • Definition: Waiting for Input/Output (disk, network, database).
  • Examples: API calls, file read/write, database queries.
  • Best optimization: Async/await.

C# I/O-bound Example

using System.Net.Http;

static async Task FetchDataAsync()
{
    using var client = new HttpClient();
    string result = await client.GetStringAsync("https://jsonplaceholder.typicode.com/posts/1");
    Console.WriteLine(result);
}

5️⃣ Visual Timeline

Time →
──────────────────────────────────────────────────────────

CPU-bound (single thread):
[######## CPU Busy ########][######## CPU Busy ########]

I/O-bound (sync):
[Send Request][Idle........waiting........][Process Data]

I/O-bound (async):
[Send Request] → (CPU অন্য কাজ করছে) → [Process Data]

6️⃣ Summary Table

Feature Synchronous Asynchronous
Execution order One by one Overlapping tasks
Performance Slower for I/O Faster for I/O
CPU usage Idle during wait More efficient
Complexity Simple Slightly harder
Use case Simple scripts UI apps, servers

Key takeaway

  • If your work is I/O-bound → use async/await.
  • If your work is CPU-bound → use parallel processing or multi-threading.