In C#, the Thread class in the System.Threading namespace allows you to create and manage threads.
Threads are used to perform tasks concurrently, allowing you to run multiple parts of a program simultaneously.
This tutorial will guide you through creating and using threads in C#, covering:
1. Creating and Starting a Thread
To create a thread, you need to define its behavior in a method and create a Thread object that runs that method.
using System; using System.Threading; class Program { static void Main() { Thread thread = new Thread(PrintNumbers); Console.WriteLine("Starting thread..."); thread.Start(); // Starts the thread Console.WriteLine("Thread started."); } static void PrintNumbers() { for (int i = 1; i <= 5; i++) { Console.WriteLine($"Number: {i}"); Thread.Sleep(500); // Simulate some work } } }
Output:
Starting thread... Thread started. Number: 1 Number: 2 Number: 3 Number: 4 Number: 5
In this example:
- The PrintNumbers method is executed on a separate thread.
- Thread.Sleep(500); pauses the thread for half a second in each loop iteration.
2. Passing Parameters to a Thread
You can pass parameters to a thread using the ParameterizedThreadStart delegate or by using lambda expressions.
Using ParameterizedThreadStart
using System; using System.Threading; class Program { static void Main() { Thread thread = new Thread(PrintMessage); thread.Start("Hello from thread!"); // Passes a message to the thread } static void PrintMessage(object message) { Console.WriteLine(message); } }
Output:
Hello from thread!
In this example:
- The message “Hello from thread!” is passed to PrintMessage.
- PrintMessage accepts a parameter of type object, allowing it to receive any type.
Using Lambda Expression
using System; using System.Threading; class Program { static void Main() { int number = 10; Thread thread = new Thread(() => PrintNumbers(number)); thread.Start(); } static void PrintNumbers(int max) { for (int i = 1; i <= max; i++) { Console.WriteLine($"Number: {i}"); } } }
Output:
Number: 1 Number: 2 ... Number: 10
In this example:
- We use a lambda expression to pass an int parameter (number) to the PrintNumbers method.
3. Synchronizing Threads with Join
The Join method makes the calling thread wait until the specified thread completes its work.
using System; using System.Threading; class Program { static void Main() { Thread thread = new Thread(PrintNumbers); Console.WriteLine("Starting thread..."); thread.Start(); thread.Join(); // Waits for the thread to complete Console.WriteLine("Thread completed."); } static void PrintNumbers() { for (int i = 1; i <= 5; i++) { Console.WriteLine($"Number: {i}"); Thread.Sleep(500); } } }
Output:
Starting thread... Number: 1 Number: 2 Number: 3 Number: 4 Number: 5 Thread completed.
In this example:
- thread.Join(); pauses the main thread until PrintNumbers completes.
4. Using Thread.Sleep to Pause a Thread
Thread.Sleep pauses the current thread for a specified amount of time.
using System; using System.Threading; class Program { static void Main() { Thread thread = new Thread(PrintNumbers); thread.Start(); } static void PrintNumbers() { for (int i = 1; i <= 5; i++) { Console.WriteLine($"Number: {i}"); Thread.Sleep(1000); // Pauses the thread for 1 second } } }
Output:
Number: 1 # 1-second pause Number: 2 # 1-second pause ...
In this example:
- Each call to Thread.Sleep(1000); pauses the thread for one second.
5. Using ThreadPriority
ThreadPriority allows you to set the priority of a thread, influencing its scheduling.
using System; using System.Threading; class Program { static void Main() { Thread highPriorityThread = new Thread(() => PrintNumbers("High Priority")); highPriorityThread.Priority = ThreadPriority.Highest; Thread lowPriorityThread = new Thread(() => PrintNumbers("Low Priority")); lowPriorityThread.Priority = ThreadPriority.Lowest; highPriorityThread.Start(); lowPriorityThread.Start(); } static void PrintNumbers(string priority) { for (int i = 1; i <= 3; i++) { Console.WriteLine($"{priority} - Number: {i}"); Thread.Sleep(500); } } }
Output:
High Priority - Number: 1 Low Priority - Number: 1 High Priority - Number: 2 Low Priority - Number: 2 ...
In this example:
- highPriorityThread.Priority = ThreadPriority.Highest; and lowPriorityThread.Priority = ThreadPriority.Lowest; adjust the scheduling priorities.
- Priority does not guarantee exact execution order but can affect scheduling.
6. Thread-Safe Operations Using Locks
When multiple threads access shared resources, use a lock statement to ensure only one thread accesses the resource at a time.
using System; using System.Threading; class Program { private static int counter = 0; private static readonly object lockObj = new object(); static void Main() { Thread thread1 = new Thread(IncrementCounter); Thread thread2 = new Thread(IncrementCounter); thread1.Start(); thread2.Start(); thread1.Join(); thread2.Join(); Console.WriteLine($"Final counter value: {counter}"); } static void IncrementCounter() { for (int i = 0; i < 5; i++) { lock (lockObj) { counter++; Console.WriteLine($"Counter: {counter}"); } Thread.Sleep(100); // Simulate work } } }
Output:
Counter: 1 Counter: 2 Counter: 3 ... Final counter value: 10
In this example:
- The lock statement ensures that only one thread can increment counter at a time.
- lock (lockObj) prevents race conditions by serializing access to counter.
Summary
In this tutorial, we covered the basics of creating and using threads in C#.
Here’s a quick recap:
- Creating and Starting a Thread: Using Thread to run a method concurrently.
- Passing Parameters to a Thread: Using ParameterizedThreadStart and lambda expressions to pass parameters.
- Synchronizing Threads with Join: Making one thread wait for another to complete.
- Using Thread.Sleep to Pause a Thread: Temporarily pausing a thread.
- Using ThreadPriority: Adjusting the priority of a thread to influence scheduling.
- Thread-Safe Operations Using Locks: Using lock to ensure thread safety when accessing shared resources.
By understanding and applying these thread techniques, you can create responsive and efficient multithreaded applications in C#.