多线程编程
1. 概述
多线程编程是指在一个程序中同时执行多个线程,每个线程独立运行,可以并行处理不同的任务。多线程编程可以提高程序的响应性和性能,特别是在处理I/O密集型任务或需要并行计算的场景中。
1.1 为什么需要多线程?
- 提高响应性:在单线程程序中,如果一个任务需要长时间执行,整个程序会阻塞,导致用户界面无响应。多线程可以将这些长时间任务放在后台线程中执行,保持用户界面的响应性。
- 提高性能:多线程可以充分利用多核处理器的并行处理能力,加快计算密集型任务的执行速度。
1.2 多线程的挑战
- 线程安全:多个线程同时访问共享资源时,可能会导致数据不一致或竞争条件。
- 死锁:两个或多个线程互相等待对方释放资源,导致程序无法继续执行。
- 资源管理:线程的创建和销毁需要消耗系统资源,过多的线程可能导致资源耗尽。
2. C# 中的多线程编程
C# 提供了丰富的多线程编程支持,包括 Thread 类、Task 类、async/await 等。
2.1 使用 Thread 类
Thread 类是 C# 中用于创建和管理线程的基本类。
2.1.1 创建线程
using System;
using System.Threading;
class Program
{
static void Main()
{
// 创建一个新的线程
Thread thread = new Thread(new ThreadStart(DoWork));
// 启动线程
thread.Start();
// 主线程继续执行
Console.WriteLine("Main thread is running...");
// 等待线程完成
thread.Join();
Console.WriteLine("Main thread is done.");
}
static void DoWork()
{
Console.WriteLine("Worker thread is running...");
Thread.Sleep(2000); // 模拟工作
Console.WriteLine("Worker thread is done.");
}
}
2.1.2 传递参数给线程
using System;
using System.Threading;
class Program
{
static void Main()
{
// 创建一个新的线程,并传递参数
Thread thread = new Thread(new ParameterizedThreadStart(DoWork));
// 启动线程
thread.Start("Hello from Main");
// 主线程继续执行
Console.WriteLine("Main thread is running...");
// 等待线程完成
thread.Join();
Console.WriteLine("Main thread is done.");
}
static void DoWork(object param)
{
string message = (string)param;
Console.WriteLine("Worker thread received: " + message);
Thread.Sleep(2000); // 模拟工作
Console.WriteLine("Worker thread is done.");
}
}
2.2 使用 Task 类
Task 类是 .NET 中用于异步编程的高级抽象,提供了更强大的功能和更好的性能。
2.2.1 创建和启动任务
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// 创建一个任务
Task task = Task.Run(() => DoWork());
// 主线程继续执行
Console.WriteLine("Main thread is running...");
// 等待任务完成
await task;
Console.WriteLine("Main thread is done.");
}
static void DoWork()
{
Console.WriteLine("Worker task is running...");
Thread.Sleep(2000); // 模拟工作
Console.WriteLine("Worker task is done.");
}
}
2.2.2 返回任务结果
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// 创建一个任务并返回结果
Task<int> task = Task.Run(() => CalculateSum(10, 20));
// 主线程继续执行
Console.WriteLine("Main thread is running...");
// 等待任务完成并获取结果
int result = await task;
Console.WriteLine("Sum result: " + result);
Console.WriteLine("Main thread is done.");
}
static int CalculateSum(int a, int b)
{
Console.WriteLine("Calculating sum...");
Thread.Sleep(2000); // 模拟工作
return a + b;
}
}
2.3 使用 async/await
async/await 是 C# 中用于简化异步编程的关键字,使得异步代码看起来像同步代码一样。
2.3.1 异步方法
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
Console.WriteLine("Main thread is running...");
// 调用异步方法
await DoWorkAsync();
Console.WriteLine("Main thread is done.");
}
static async Task DoWorkAsync()
{
Console.WriteLine("Async method is starting...");
await Task.Delay(2000); // 模拟异步工作
Console.WriteLine("Async method is done.");
}
}
2.3.2 返回异步结果
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
Console.WriteLine("Main thread is running...");
// 调用异步方法并获取结果
int result = await CalculateSumAsync(10, 20);
Console.WriteLine("Sum result: " + result);
Console.WriteLine("Main thread is done.");
}
static async Task<int> CalculateSumAsync(int a, int b)
{
Console.WriteLine("Calculating sum asynchronously...");
await Task.Delay(2000); // 模拟异步工作
return a + b;
}
}
3. 线程同步
在多线程编程中,线程同步是一个重要的问题。多个线程同时访问共享资源时,可能会导致数据不一致或竞争条件。C# 提供了多种线程同步机制。
3.1 lock 语句
lock 语句用于锁定一个对象,确保同一时间只有一个线程可以访问被锁定的代码块。
using System;
using System.Threading.Tasks;
class Program
{
private static readonly object _lock = new object();
private static int _counter = 0;
static async Task Main()
{
Task task1 = Task.Run(() => IncrementCounter());
Task task2 = Task.Run(() => IncrementCounter());
await Task.WhenAll(task1, task2);
Console.WriteLine("Counter: " + _counter);
}
static void IncrementCounter()
{
for (int i = 0; i < 100000; i++)
{
lock (_lock)
{
_counter++;
}
}
}
}
3.2 Monitor 类
Monitor 类提供了更灵活的锁定机制,可以手动控制锁定和解锁。
using System;
using System.Threading.Tasks;
class Program
{
private static readonly object _lock = new object();
private static int _counter = 0;
static async Task Main()
{
Task task1 = Task.Run(() => IncrementCounter());
Task task2 = Task.Run(() => IncrementCounter());
await Task.WhenAll(task1, task2);
Console.WriteLine("Counter: " + _counter);
}
static void IncrementCounter()
{
for (int i = 0; i < 100000; i++)
{
bool lockTaken = false;
try
{
Monitor.Enter(_lock, ref lockTaken);
_counter++;
}
finally
{
if (lockTaken)
{
Monitor.Exit(_lock);
}
}
}
}
}
3.3 Mutex 类
Mutex 类用于跨进程的线程同步,可以确保同一时间只有一个线程可以访问共享资源。
using System;
using System.Threading;
class Program
{
private static Mutex _mutex = new Mutex();
private static int _counter = 0;
static void Main()
{
for (int i = 0; i < 5; i++)
{
Thread thread = new Thread(IncrementCounter);
thread.Start();
}
}
static void IncrementCounter()
{
for (int i = 0; i < 100000; i++)
{
_mutex.WaitOne();
_counter++;
_mutex.ReleaseMutex();
}
Console.WriteLine("Counter: " + _counter);
}
}
4. 实践练习
4.1 练习1:并行计算
编写一个程序,使用多线程并行计算一组数字的平方和。
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sum = await CalculateSquareSumAsync(numbers);
Console.WriteLine("Sum of squares: " + sum);
}
static async Task<int> CalculateSquareSumAsync(int[] numbers)
{
int sum = 0;
object _lock = new object();
await Task.Run(() =>
{
Parallel.ForEach(numbers, number =>
{
int square = number * number;
lock (_lock)
{
sum += square;
}
});
});
return sum;
}
}
4.2 练习2:生产者-消费者模型
编写一个程序,实现生产者-消费者模型。生产者线程生成数据并放入队列,消费者线程从队列中取出数据并处理。
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
class Program
{
private static BlockingCollection<int> _queue = new BlockingCollection<int>();
static async Task Main()
{
Task producer = Task.Run(() => Producer());
Task consumer = Task.Run(() => Consumer());
await Task.WhenAll(producer, consumer);
}
static void Producer()
{
for (int i = 0; i < 10; i++)
{
_queue.Add(i);
Console.WriteLine("Produced: " + i);
Thread.Sleep(100);
}
_queue.CompleteAdding();
}
static void Consumer()
{
foreach (int item in _queue.GetConsumingEnumerable())
{
Console.WriteLine("Consumed: " + item);
Thread.Sleep(200);
}
}
}
5. 总结
多线程编程是提高程序性能和响应性的重要手段。C# 提供了丰富的多线程编程工具,包括 Thread、Task、async/await 等。在多线程编程中,线程同步是一个关键问题,需要合理使用 lock、Monitor、Mutex 等机制来确保线程安全。
通过本教程的学习,你应该能够理解多线程编程的基本概念,掌握 C# 中的多线程编程技术,并能够编写简单的多线程程序。