在 C# 多线程编程中,当多个线程访问共享资源时,可能会出现数据不一致或其他并发问题,这时就需要使用互斥机制来确保同一时间只有一个线程可以访问共享资源。以下是几种常见的 C# 多线程互斥方法:
lock 语句是 C# 中最常用的实现线程互斥的方式,它基于 Monitor 类实现,使用简单方便。
using System;
using System.Threading;
class Program
{
private static readonly object lockObject = new object();
private static int sharedResource = 0;
static void Main()
{
Thread t1 = new Thread(Increment);
Thread t2 = new Thread(Increment);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine($"Shared resource value: {sharedResource}");
}
static void Increment()
{
for (int i = 0; i < 100000; i++)
{
lock (lockObject)
{
sharedResource++;
}
}
}
}
解释:
lockObject 是一个用于同步的对象,通常是一个私有静态的 object 类型实例。
lock 语句会获取 lockObject 的锁,如果锁已被其他线程持有,则当前线程会阻塞,直到锁被释放。
- 在
lock 语句块内的代码是临界区,同一时间只有一个线程可以执行该代码块,从而保证了对 sharedResource 的安全访问。
Monitor 类提供了更底层的线程同步机制,
lock 语句实际上是
Monitor 类的语法糖。
using System;
using System.Threading;
class Program
{
private static readonly object monitorObject = new object();
private static int sharedValue = 0;
static void Main()
{
Thread t1 = new Thread(UpdateValue);
Thread t2 = new Thread(UpdateValue);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine($"Shared value: {sharedValue}");
}
static void UpdateValue()
{
for (int i = 0; i < 100000; i++)
{
Monitor.Enter(monitorObject);
try
{
sharedValue++;
}
finally
{
Monitor.Exit(monitorObject);
}
}
}
}
解释:
Monitor.Enter 方法用于获取指定对象的锁,如果锁已被其他线程持有,则当前线程会阻塞。
try-finally 块确保无论是否发生异常,Monitor.Exit 方法都会被调用,从而释放锁。
Mutex(互斥体)是一种跨进程的同步原语,可用于在多个进程或线程之间实现互斥访问。
using System;
using System.Threading;
class Program
{
private static readonly Mutex mutex = new Mutex();
private static int counter = 0;
static void Main()
{
Thread t1 = new Thread(Count);
Thread t2 = new Thread(Count);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine($"Counter value: {counter}");
}
static void Count()
{
for (int i = 0; i < 100000; i++)
{
mutex.WaitOne();
try
{
counter++;
}
finally
{
mutex.ReleaseMutex();
}
}
}
}
解释:
mutex.WaitOne 方法用于请求获取互斥体的所有权,如果互斥体已被其他线程或进程持有,则当前线程会阻塞。
mutex.ReleaseMutex 方法用于释放互斥体的所有权,允许其他线程或进程获取该互斥体。
SemaphoreSlim 是一个轻量级的信号量,可用于限制同时访问共享资源的线程数量。当将最大计数设置为 1 时,它可以作为互斥锁使用。
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
private static readonly SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
private static int sharedData = 0;
static async Task Main()
{
Task t1 = Task.Run(UpdateData);
Task t2 = Task.Run(UpdateData);
await Task.WhenAll(t1, t2);
Console.WriteLine($"Shared data value: {sharedData}");
}
static async Task UpdateData()
{
for (int i = 0; i < 100000; i++)
{
await semaphore.WaitAsync();
try
{
sharedData++;
}
finally
{
semaphore.Release();
}
}
}
}
解释:
SemaphoreSlim 的构造函数第一个参数表示初始计数,第二个参数表示最大计数。当最大计数为 1 时,它就相当于一个互斥锁。
semaphore.WaitAsync 方法用于请求获取信号量,如果信号量计数为 0,则当前线程会等待。
semaphore.Release 方法用于释放信号量,将计数加 1。