CACS408 Dot Net Technology

Dot Net TechnologyUnit 711 min read

Multithreading & Exception Handling in C: Threads, Events, Delegates, and Error Recovery

Unit 7 of Dot Net Technology covers multithreading (thread lifecycle, synchronization, Thread and Task classes) and exception handling (try-catch-finally, custom exceptions, pre-built delegates like Func<T> and Action<T>) with real-world applications in banking, web apps, and file I/O. Includes code examples, state dia

Key Concepts and Definitions

Multithreading Basics

Thread: The smallest unit of execution within a process. A process can have multiple threads running concurrently. Process: An instance of a program in execution (e.g., a running application). Concurrency vs Parallelism:

  • Concurrency: Multiple threads appear to run simultaneously (e.g., UI thread handling user input while another thread processes data).
  • Parallelism: Multiple threads run truly simultaneously on multi-core CPUs.
mindmap
  root((Multithreading))
    Concepts
      Thread: Smallest execution unit
      Process: Instance of a program
      Concurrency: Appearance of simultaneous execution
      Parallelism: True simultaneous execution
    Lifecycle
      New --> Runnable --> Running --> Blocked/Waiting --> Terminated
    Synchronization
      Locks (lock, Monitor)
      Interlocked class
      Thread-safe collections
    Classes
      Thread
      Task (TPL)
      ThreadPool

Thread Lifecycle in C#

The lifecycle of a thread in C# can be visualized as follows:

stateDiagram-v2
  [*] --> New: Thread created but not started
  New --> Runnable: Start() called
  Runnable --> Running: Thread begins execution
  Running --> Blocked: Waiting for I/O or lock
  Running --> Waiting: Sleep() or Wait()
  Blocked --> Runnable: Resource available
  Waiting --> Runnable: Timeout or signal
  Running --> Terminated: Thread completes
  [*]

Example: Thread Lifecycle Demonstration

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Thread t = new Thread(PrintNumbers);
        Console.WriteLine($"Thread state before start: {t.ThreadState}"); // New
        t.Start();
        Console.WriteLine($"Thread state after start: {t.ThreadState}"); // Runnable/Running
        Thread.Sleep(1000); // Let the thread run
        Console.WriteLine($"Thread state after sleep: {t.ThreadState}"); // Terminated
    }

    static void PrintNumbers()
    {
        for (int i = 1; i <= 5; i++)
        {
            Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} printing: {i}");
            Thread.Sleep(500); // Simulate work
        }
    }
}

Output Trace:

Step Thread State Action Console Output
1 New Thread created Thread state before start: New
2 Runnable/Running t.Start() called Thread state after start: Running
3 Running Thread executes PrintNumbers Prints numbers 1 to 5 with delays
4 Terminated Thread completes Thread state after sleep: Terminated

Synchronization in Multithreading

Race Condition: Occurs when multiple threads access shared data and try to change it simultaneously, leading to unpredictable results. Solution: Use synchronization mechanisms like lock, Monitor, or thread-safe collections.

Example: Race Condition in Bank Balance

class BankAccount
{
    private int balance;
    private readonly object lockObj = new object();

    public void Deposit(int amount)
    {
        lock (lockObj)
        {
            balance += amount;
            Console.WriteLine($"Deposited {amount}. New balance: {balance}");
        }
    }

    public void Withdraw(int amount)
    {
        lock (lockObj)
        {
            if (balance >= amount)
            {
                balance -= amount;
                Console.WriteLine($"Withdrew {amount}. New balance: {balance}");
            }
            else
            {
                Console.WriteLine("Insufficient balance!");
            }
        }
    }
}

State After Each Operation:

sequenceDiagram
    participant User1 as Thread 1 (Deposit)
    participant User2 as Thread 2 (Withdraw)
    participant Account as BankAccount
    User1->>Account: Deposit(1000)
    Account-->>User1: Balance: 1000 (Locked)
    User2->>Account: Withdraw(500)
    Account-->>User2: Balance: 500 (Locked)
    User1->>Account: Deposit(200)
    Account-->>User1: Balance: 700 (Locked)

Exception Handling in C#

Exceptions are events that disrupt the normal flow of a program. C# provides structured exception handling using try, catch, finally, and throw.

Types of Exceptions

  1. System Exceptions: Built-in exceptions (e.g., NullReferenceException, IndexOutOfRangeException).
  2. Application Exceptions: Custom exceptions created by developers.

Example: Handling Bank Withdrawal Exceptions

using System;

class InsufficientFundsException : Exception
{
    public InsufficientFundsException(string message) : base(message) { }
}

class BankAccount
{
    private decimal balance;

    public BankAccount(decimal initialBalance)
    {
        balance = initialBalance;
    }

    public void Withdraw(decimal amount)
    {
        if (amount > balance)
        {
            throw new InsufficientFundsException($"Insufficient funds. Current balance: {balance}");
        }
        balance -= amount;
        Console.WriteLine($"Withdrew {amount}. Remaining balance: {balance}");
    }
}

class Program
{
    static void Main()
    {
        BankAccount account = new BankAccount(1000);
        try
        {
            account.Withdraw(1500); // Throws InsufficientFundsException
        }
        catch (InsufficientFundsException ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Unexpected error: {ex.Message}");
        }
        finally
        {
            Console.WriteLine("Transaction attempted.");
        }
    }
}

Output Trace:

Step Action Exception Thrown Output
1 account.Withdraw(1500) InsufficientFundsException Error: Insufficient funds. Current balance: 1000
2 catch block - Transaction attempted. (finally block)

Events and Delegates in C#

Delegates

Delegates are type-safe function pointers in C#. They enable passing methods as arguments.

Generic Delegates

  1. Action<T>: Used for methods that return void.
  2. Func<T>: Used for methods that return a value.

Example: Lambda Expression with Generic Delegate

using System;

class Program
{
    static void Main()
    {
        // Using Action<T> (void return)
        Action<int> printSquare = x => Console.WriteLine($"Square of {x} is {x * x}");
        printSquare(5); // Output: Square of 5 is 25

        // Using Func<T> (returns int)
        Func<int, int, int> add = (a, b) => a + b;
        Console.WriteLine($"Sum: {add(3, 4)}"); // Output: Sum: 7
    }
}

In the Real World

  1. eSewa (Nepal):

    • Multithreading: eSewa processes thousands of transactions simultaneously. Background threads handle payment validations, SMS notifications, and database updates concurrently to ensure quick responses.
    • Exception Handling: Custom exceptions are thrown for invalid transactions (e.g., insufficient balance, expired card). These are logged and user-friendly messages are displayed.
  2. Khalti (Nepal):

    • Thread Pool: Khalti uses thread pools to manage multiple payment requests efficiently. For example, during Diwali sales, hundreds of users might request payments at once. Thread pools ensure these requests are processed without overwhelming the system.
    • Synchronization: Shared resources like user accounts or transaction logs are protected using locks to prevent race conditions.
  3. Pathao (Nepal):

    • Multithreading: Pathao’s backend uses multithreading to handle ride requests, driver assignments, and GPS updates in parallel. For example, while one thread processes a new ride request, another thread updates the driver’s location on the map.
    • Exception Handling: If a driver’s GPS signal is lost, Pathao throws a custom exception (GpsSignalLostException) and notifies the driver to restart the app or check their connection.
  4. NTC (Nepal Telecom):

    • Task Parallel Library (TPL): NTC’s customer service portal uses Task for parallel processing of bulk SMS or email notifications. For example, sending a promotional offer to 10,000 subscribers is divided into smaller tasks executed concurrently.
    • Custom Exceptions: If a subscriber’s number is invalid, NTC throws a InvalidPhoneNumberException and skips that number in the batch.

Exam Tip

  1. Thread Lifecycle: Always explain the states (New, Runnable, Running, Blocked, Terminated) with a diagram or code snippet. Examiners love seeing state transitions.
  2. Synchronization: Use lock in examples to prevent race conditions. Compare lock with Monitor and explain when to use each.
  3. Exception Handling:
    • Differentiate between try-catch-finally and using blocks.
    • Write custom exceptions with inheritance from Exception and include constructors.
    • Handle common exceptions like NullReferenceException, IndexOutOfRangeException, and FormatException.
  4. Delegates and Lambda Expressions:
    • Show how Action<T> and Func<T> are used with lambda expressions.
    • Explain generic delegates with a practical example (e.g., sorting a list with a custom comparator).
  5. Real-World Scenarios:
    • Tie examples to banking (e.g., concurrent transactions), web apps (e.g., handling user requests), or file I/O (e.g., reading/writing logs).
    • For multithreading, use Thread.Sleep() to simulate delays and demonstrate thread interleaving.

Common Pitfalls to Avoid

  • Deadlocks: Always release locks in a finally block or use try-finally to avoid deadlocks.
    lock (lockObj1)
    {
        lock (lockObj2) { /* Critical section */ }
    }
    
  • Ignoring Exceptions: Never swallow exceptions with an empty catch block. At least log the error.
  • Thread Safety: Assume shared data is unsafe unless proven otherwise. Use lock or thread-safe collections like ConcurrentQueue<T>.

Practice Questions for Exam

  1. Write a C# program to demonstrate thread synchronization using lock for a shared counter.
  2. Create a custom exception InvalidAgeException and handle it in a method that validates user age.
  3. Explain the difference between Thread and Task classes. When would you use each?
  4. Write a program that uses Action<T> to process a list of strings (e.g., convert to uppercase).
  5. How would you handle a DivideByZeroException in a calculator application? Show the code.

Summary Table: Key Concepts

Concept Description Example Use Case
Thread Lightweight process for concurrent execution. Pathao handling multiple ride requests.
Lock Synchronization mechanism to prevent race conditions. Bank account balance updates.
Exception Event that disrupts normal flow. Invalid user input in eSewa.
Delegate Type-safe function pointer. Event handlers in WinForms.
Lambda Expression Anonymous function for concise syntax. LINQ queries in C#.
Task Higher-level abstraction for asynchronous programming. NTC sending bulk SMS notifications.

Based on the TU BCA syllabus for Dot Net Technology (CACS408), unit 7.

Discussion

Loading…