In C#, threads are a way to achieve concurrent and parallel programming. They allow us to split the execution of a program into multiple tasks that can run simultaneously.
In many cases, we need to perform tasks simultaneously, such as accessing a database while displaying an animation on the user interface. This is where threads allow us to divide the work into concurrent tasks.
A thread is the smallest unit of processing in a program. Normally, a program has at least one thread, called the main thread, which is responsible for executing instructions in a sequential order.
Create and run a Thread
In C#, a thread can be created by instantiating the Thread
class and assigning it a method to execute. Then, we call the thread by invoking the .Start()
method.
using System;
using System.Threading;
public class Program
{
public static void Main()
{
// Create a new thread
Thread newThread = new Thread(BackgroundTask);
newThread.Start(); // Start the thread
// Task in the main thread
for (int i = 0; i < 5; i++)
{
Console.WriteLine("Main thread running...");
Thread.Sleep(1000);
}
}
public static void BackgroundTask()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("Secondary thread running...");
Thread.Sleep(1000);
}
}
}
The method that will run in the new thread must be of type void
and should not have parameters (or parameters should be handled alternatively).
Useful Thread methods and properties
The Thread
class provides several useful methods and properties:
Thread.Sleep(int milliseconds)
Suspends the execution of the current thread for the specified number of milliseconds. Useful for making pauses.
Thread.Join()
Makes the main thread wait for the secondary thread to complete its task before continuing. This is useful to ensure that a thread has finished before proceeding with execution.
IsAlive
Property that indicates whether the thread is running.
Priority
Allows setting the thread’s priority (Low, Normal, High), which affects the relative order of execution.
Thread newThread = new Thread(BackgroundTask);
newThread.Start();
// Wait for the new thread to finish before continuing
newThread.Join();
Console.WriteLine("Secondary thread finished. Continuing in the main thread.");
Thread synchronization
Thread synchronization is essential to avoid issues such as race conditions or deadlocks.
Using the lock
keyword in C# allows only one thread to access a shared resource at a time, thus preventing conflicts.
using System;
using System.Threading;
public class BankAccount
{
private decimal balance = 0;
private object lockObject = new object();
public void Deposit(decimal amount)
{
lock (lockObject)
{
balance += amount;
Console.WriteLine($"Deposit of {amount}. Current balance: {balance}");
}
}
}
public class Program
{
public static void Main()
{
BankAccount account = new BankAccount();
// Create multiple threads to simulate simultaneous deposits
Thread thread1 = new Thread(() => account.Deposit(100));
Thread thread2 = new Thread(() => account.Deposit(200));
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
}
}
In this example, the lock
keyword ensures that only one thread can access the deposit operation at a time.