C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
194: The Thread Class Directly
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
boolflag) 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.
There are no comments for now.