15 minlesson

The Thread Class

The Thread Class

The System.Threading.Thread class provides direct control over thread creation and management. While modern C# typically uses Tasks, understanding the Thread class helps you appreciate how async works under the hood.

Creating a Thread

There are several ways to create a thread:

Using a Named Method

csharp
1void DoWork()
2{
3 Console.WriteLine("Working on thread: " + Thread.CurrentThread.ManagedThreadId);
4}
5
6Thread thread = new Thread(DoWork);
7thread.Start();

Using a Lambda Expression

csharp
1Thread thread = new Thread(() =>
2{
3 Console.WriteLine("Lambda running on thread: " + Thread.CurrentThread.ManagedThreadId);
4});
5thread.Start();

Passing Data to a Thread

csharp
1// Using ParameterizedThreadStart
2Thread thread = new Thread((obj) =>
3{
4 string message = (string)obj!;
5 Console.WriteLine(message);
6});
7thread.Start("Hello from parameter!");
8
9// Using closure (preferred)
10string data = "Hello from closure!";
11Thread thread2 = new Thread(() =>
12{
13 Console.WriteLine(data);
14});
15thread2.Start();

Essential Thread Methods

Start()

Begins execution of the thread:

csharp
1Thread thread = new Thread(DoWork);
2thread.Start(); // Thread begins executing DoWork

Join()

Blocks the calling thread until this thread completes:

csharp
1Thread thread = new Thread(DoLongWork);
2thread.Start();
3
4// Do other work while thread runs...
5
6thread.Join(); // Wait for thread to complete
7Console.WriteLine("Thread finished!");

Join with Timeout

csharp
1Thread thread = new Thread(DoLongWork);
2thread.Start();
3
4bool completed = thread.Join(5000); // Wait max 5 seconds
5if (!completed)
6{
7 Console.WriteLine("Thread is still running after 5 seconds");
8}

Sleep()

Pauses the current thread:

csharp
1Thread.Sleep(1000); // Sleep for 1 second
2Thread.Sleep(TimeSpan.FromSeconds(2)); // Sleep for 2 seconds

Thread Properties

csharp
1Thread current = Thread.CurrentThread;
2
3// Unique identifier for this thread
4int id = current.ManagedThreadId;
5
6// Thread name (useful for debugging)
7current.Name = "WorkerThread";
8
9// Is this a background thread?
10bool isBackground = current.IsBackground;
11
12// Current state
13ThreadState state = current.ThreadState;
14
15// Is thread still running?
16bool isAlive = current.IsAlive;

Foreground vs Background Threads

csharp
1// Foreground thread (default)
2// App won't exit until this completes
3Thread foreground = new Thread(DoWork);
4foreground.Start();
5
6// Background thread
7// App can exit while this is running
8Thread background = new Thread(DoWork);
9background.IsBackground = true;
10background.Start();

Important: The application terminates when all foreground threads complete. Background threads are terminated abruptly.

Complete Example

csharp
1Console.WriteLine($"Main thread: {Thread.CurrentThread.ManagedThreadId}");
2
3// Create multiple threads
4Thread[] threads = new Thread[3];
5
6for (int i = 0; i < 3; i++)
7{
8 int threadNumber = i; // Capture for closure
9 threads[i] = new Thread(() =>
10 {
11 Console.WriteLine($"Thread {threadNumber} starting on {Thread.CurrentThread.ManagedThreadId}");
12 Thread.Sleep(1000); // Simulate work
13 Console.WriteLine($"Thread {threadNumber} finished");
14 });
15 threads[i].Name = $"Worker-{i}";
16 threads[i].Start();
17}
18
19// Wait for all threads to complete
20foreach (Thread t in threads)
21{
22 t.Join();
23}
24
25Console.WriteLine("All threads completed!");

Key Takeaways

  • Thread.Start() begins execution
  • Thread.Join() waits for completion
  • Thread.Sleep() pauses the current thread
  • Background threads don't prevent app exit
  • Use closures carefully - capture loop variables properly
  • Modern C# prefers Tasks over direct Thread usage