Skip to Content
Course content

194: The Thread Class Directly

Click on the "Edit" button in the top corner of the screen to edit your slide content.

You've likely spent most of your time with Task and async/await. In modern C#, that's usually the right call. But as a software engineer, you need to understand what's happening under the hood. The System.Threading.Thread class is the raw, low-level primitive that Task is built upon. It gives you direct control over the execution thread, which is occasionally necessary for long-running background services or when you need to set a specific stack size or thread priority.

To see this in action, let's build a simple "System Heartbeat" monitor. We want a piece of code that runs in the background, printing a "Pulse" message to the console every few seconds, while the main application continues to take user input.

Defining the Heartbeat Logic

First, I'll write a method that represents the work we want to do on our separate thread. This is just a simple loop that sleeps and prints. I'm keeping it in a separate method because the Thread constructor expects a ThreadStart delegate—basically, a method that takes no arguments and returns nothing.

void RunHeartbeat()
{
    while (true)
    {
        Console.WriteLine($"[Heartbeat] System is alive at {DateTime.Now:HH:mm:ss}");
        Thread.Sleep(2000); // Pause for 2 seconds
    }
}

Spawning the Manual Thread

Now, I'll instantiate the Thread class and pass that method into it. Unlike a Task, which is queued to the ThreadPool, creating a Thread object allocates a brand new operating system thread immediately upon calling Start().

Thread heartbeatThread = new Thread(RunHeartbeat);
heartbeatThread.Start();

Console.WriteLine("Main thread is running. Press Enter to exit...");
Console.ReadLine();

Dealing with the "Zombies" (My Mistake)

I just ran this code, and I hit a common snag. I noticed that if I commented out the Console.ReadLine() or if the main method finished, the application didn't actually exit. The console window stayed open, and the heartbeat kept printing.

Here is why: by default, a manually created Thread is a Foreground Thread. The .NET runtime will not shut down the process until all foreground threads have finished. Since my RunHeartbeat method has a while(true) loop, it never finishes, effectively holding the whole application hostage.

To fix this, I need to set the IsBackground property to true. Background threads are terminated automatically by the runtime as soon as all foreground threads (like the main thread) finish. Let's fix the initialization:

Thread heartbeatThread = new Thread(RunHeartbeat);
heartbeatThread.IsBackground = true; // This is the magic line
heartbeatThread.Start();

Stopping the Thread Gracefully

While IsBackground = true solves the shutdown problem, it's a bit like pulling the power plug on a computer. If this thread were writing to a file or database, we'd risk data corruption. In a real-world scenario, I'd use a CancellationToken or a volatile boolean flag to tell the thread to stop loop execution cleanly.

For this example, I'll add a simple bool flag so we can shut down the heartbeat before the main thread exits.

bool _keepRunning = true;

void RunHeartbeat()
{
    while (_keepRunning)
    {
        Console.WriteLine($"[Heartbeat] System is alive at {DateTime.Now:HH:mm:ss}");
        Thread.Sleep(2000);
    }
    Console.WriteLine("Heartbeat thread shutting down cleanly.");
}

// ... in Main ...
Thread heartbeatThread = new Thread(RunHeartbeat);
heartbeatThread.IsBackground = true;
heartbeatThread.Start();

Console.ReadLine();
_keepRunning = false; // Signal the thread to stop
heartbeatThread.Join(); // Wait for the thread to actually finish
Console.WriteLine("Main thread exiting.");

By calling Join(), the main thread pauses until heartbeatThread finishes its current loop iteration and exits. This is the professional way to ensure a clean shutdown.




📋 Practical Task

Build a Background Log-Cleaner Simulator

Create a program that simulates a background log-cleaning service using the Thread class. Your program should meet these requirements:

  • Create a method called CleanLogs() that prints "Cleaning old logs..." every 3 seconds.
  • Start this method on a separate Thread.
  • Ensure the thread is configured as a background thread so it doesn't prevent the application from closing.
  • Implement a mechanism (like a bool flag) to stop the cleaning process when the user types "stop" in the console.
  • Use Thread.Join() to ensure the cleaning thread has fully stopped before the application prints "System shutdown complete" and exits.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.