Concurrency is a fundamental concept in modern programming, allowing multiple tasks to be executed simultaneously. In the context of C#, managing concurrent operations efficiently can lead to significant performance improvements and better resource utilization. This tutorial will explore various aspects of concurrency in C#, including threading, asynchronous programming, and synchronization mechanisms.
Concurrency in C# is primarily managed through threads, which are lightweight units of execution that allow multiple tasks to run concurrently within a single process. The .NET framework provides several tools and libraries to facilitate concurrent programming, such as the System.Threading namespace, which includes classes for creating and managing threads, synchronization primitives, and asynchronous operations.
Let's start with a simple example of creating and starting a new thread in C#.
1using System;2using System.Threading;34class Program5{6static void Main()7{8Thread thread = new Thread(new ThreadStart(PrintNumbers));9thread.Start();1011for (int i = 1; i <= 5; i++)12{13Console.WriteLine("Main thread: " + i);14}15}1617static void PrintNumbers()18{19for (int i = 1; i <= 5; i++)20{21Console.WriteLine("Thread: " + i);22}23}24}
In this example, we create a new thread that executes the PrintNumbers method concurrently with the main thread. The output will show numbers printed alternately by both threads.
C# provides asynchronous programming support through the async and await keywords, which make it easier to write non-blocking code.
1using System;2using System.Threading.Tasks;34class Program5{6static async Task Main()7{8Console.WriteLine("Starting task...");9await PrintNumbersAsync();10Console.WriteLine("Task completed.");11}1213static async Task PrintNumbersAsync()14{15for (int i = 1; i <= 5; i++)16{17await Task.Delay(1000); // Simulate a delay18Console.WriteLine("Number: " + i);19}20}21}
In this example, the PrintNumbersAsync method is marked as async, and it uses await Task.Delay to simulate a delay without blocking the main thread. The output will show numbers printed with a one-second interval.
Synchronization is crucial when multiple threads access shared resources. C# provides several synchronization primitives, such as Mutex, Semaphore, and Monitor.
1using System;2using System.Threading;34class Program5{6static int counter = 0;7static readonly object lockObject = new object();89static void Main()10{11Thread thread1 = new Thread(IncrementCounter);12Thread thread2 = new Thread(IncrementCounter);1314thread1.Start();15thread2.Start();1617thread1.Join();18thread2.Join();1920Console.WriteLine("Final counter value: " + counter);21}2223static void IncrementCounter()24{25for (int i = 0; i < 1000; i++)26{27lock (lockObject)28{29counter++;30}31}32}33}
In this example, we use a lock statement to ensure that only one thread can execute the critical section of code at a time, preventing race conditions and ensuring that the final counter value is correct.
In the next section, we will delve deeper into threading in C#, exploring more advanced topics such as thread pools, task parallel library (TPL), and concurrent collections. Understanding these concepts will help you write more efficient and scalable applications.
Stay tuned for more tutorials on C# concurrency!