先上总结:
锁类型 | 特点 | 适用场景 | 优点 | 缺点 |
---|---|---|---|---|
自旋锁 | 忙等待实现锁定,适合高并发短时间锁定 | 高并发环境,短时间锁定,仅限单进程多线程同步 | 开销低,避免线程上下文切换 | 忙等待消耗CPU资源,不适合长时间锁定,不能跨进程使用 |
互斥锁 | 提供独占访问,阻塞等待,适合长时间锁定 | 需要跨线程或进程保护资源 | 阻塞等待不占用CPU资源,支持跨进程 | 开销高,可能导致死锁,涉及线程上下文切换 |
Redis分布式锁 | 基于Redis的分布式锁定,适合分布式系统 | 分布式环境中的资源协调 | 适用于分布式系统,提供全局一致性锁 | 依赖Redis服务,可能受网络延迟影响,需处理锁的过期和续期问题 |
lock 关键字 |
基于Monitor 的代码块级同步机制,易用 |
简单的线程同步,保护共享资源 | 语法简单,自动处理异常 | 需选择合适锁对象,粒度问题,不能跨进程使用,有线程上下文切换的开销 |
粒度:锁的粒度是指锁定代码块的范围。如果锁定范围太大,会减少并发性,影响性能。 尽量减少锁定代码块的大小,只锁定必要的部分。
自旋锁(Spin Lock)
- 特点:当线程尝试获取锁时,如果锁已经被其他线程持有,线程会不停地检查锁是否可用(即“自旋”),直到获取到锁为止。
- 优点:适用于锁定时间很短的场景,避免了线程的上下文切换,提高了性能。
- 缺点:如果锁定时间较长,自旋锁会导致CPU资源的浪费,因为线程会一直占用CPU时间进行忙等待。
- 使用场景:适用于锁定时间极短的代码段,且需要避免线程上下文切换的开销。
using System;
using System.Threading;
class SpinLockExample
{
private volatile bool locked = false;
public void Acquire()
{
while (true)
{
if (!locked)
{
locked = true;
return;
}
Thread.SpinWait(1); // 自旋等待
}
}
public void Release()
{
locked = false;
}
}
class Program
{
static SpinLockExample spinLock = new SpinLockExample();
static void CriticalSection()
{
Console.WriteLine($"{Thread.CurrentThread.Name} trying to acquire lock");
spinLock.Acquire();
Console.WriteLine($"{Thread.CurrentThread.Name} acquired lock");
// Critical section
Thread.Sleep(1000);
Console.WriteLine($"{Thread.CurrentThread.Name} releasing lock");
spinLock.Release();
}
static void Main()
{
Thread[] threads = new Thread[3];
for (int i = 0; i < threads.Length; i++)
{
threads[i] = new Thread(CriticalSection);
threads[i].Name = $"Thread {i+1}";
threads[i].Start();
}
foreach (Thread thread in threads)
{
thread.Join();
}
}
}
互斥锁 (Mutex)
特点:
- 互斥锁(Mutex)提供对资源的独占访问。线程如果无法获取锁,会被阻塞并放入等待队列。
Mutex
可以用于跨线程和跨进程的锁定。- 由操作系统管理,能够确保在一个进程中获得锁时,其他进程无法获得同一个锁。
适用场景:
- 需要在多个线程或多个进程之间同步访问共享资源。
- 需要锁定时间可能较长的操作。
- 在本地应用程序或服务中使用。
优点:
- 提供跨进程的锁定机制。
- 阻塞等待,不占用CPU资源。
- 适合长时间锁定的操作。
缺点:
- 开销较高,涉及线程上下文切换。
- 可能导致死锁,特别是在嵌套锁定的情况下。
using System;
using System.Threading;
class MutexExample
{
static Mutex mutex = new Mutex();
static void CriticalSection()
{
Console.WriteLine($"{Thread.CurrentThread.Name} trying to acquire lock");
mutex.WaitOne();
try
{
Console.WriteLine($"{Thread.CurrentThread.Name} acquired lock");
// Critical section
Thread.Sleep(1000);
}
finally
{
Console.WriteLine($"{Thread.CurrentThread.Name} releasing lock");
mutex.ReleaseMutex();
}
}
static void Main()
{
Thread[] threads = new Thread[3];
for (int i = 0; i < threads.Length; i++)
{
threads[i] = new Thread(CriticalSection);
threads[i].Name = $"Thread {i+1}";
threads[i].Start();
}
foreach (Thread thread in threads)
{
thread.Join();
}
}
}
Redis分布式锁
特点:
- Redis分布式锁利用Redis的单线程特性,通过设置键值对实现锁定。
- 支持分布式环境下的锁定机制,多个节点可以通过共享的Redis实例实现同步。
- 依赖Redis的高性能和高可用性,可以在大规模分布式系统中使用。
适用场景:
- 分布式系统中多个实例需要同步访问共享资源。
- 跨多个节点的资源协调和管理。
- 需要全局一致性的锁。
优点:
- 适用于分布式系统,提供全局一致性锁。
- 高可用性和持久性,适用于大规模分布式环境。
- 可以与Redis的其他功能(如发布/订阅、缓存)结合使用。
缺点:
- 依赖外部的Redis服务,可能受到网络延迟和服务可用性的影响。
- 实现复杂,需要处理锁的过期、自动续期等问题。
- 存在一定的性能开销,尤其是在高延迟网络环境下。
using StackExchange.Redis; using System; using System.Threading; class RedisLockExample { private static ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); private static IDatabase db = redis.GetDatabase(); private const string LockKey = "lockKey"; public static bool AcquireLock(string lockKey, TimeSpan expiry) { return db.StringSet(lockKey, "locked", expiry, When.NotExists); } public static void ReleaseLock(string lockKey) { db.KeyDelete(lockKey); } static void CriticalSection() { while (!AcquireLock(LockKey, TimeSpan.FromSeconds(5))) { Thread.Sleep(100); // wait and retry } try { Console.WriteLine($"{Thread.CurrentThread.Name} acquired lock"); // Critical section Thread.Sleep(1000); } finally { Console.WriteLine($"{Thread.CurrentThread.Name} releasing lock"); ReleaseLock(LockKey); } } static void Main() { Thread[] threads = new Thread[3]; for (int i = 0; i < threads.Length; i++) { threads[i] = new Thread(CriticalSection); threads[i].Name = $"Thread {i + 1}"; threads[i].Start(); } foreach (Thread thread in threads) { thread.Join(); } } }
lock
关键字
特点:
lock
关键字基于Monitor
实现,线程如果无法获取锁,会被阻塞并放入等待队列。- 适用于大多数普通的多线程同步场景,锁定时间可以较长。
- 自动处理异常情况,确保在异常发生时释放锁。
优点:
- 使用方便,语法简洁。
- 在等待锁时,线程不会消耗CPU资源。
- 自动处理异常,确保锁的释放。
缺点:
- 有线程上下文切换的开销。
- 需要选择合适的锁对象,避免锁定
this
或公共对象。 - 不能跨进程使用。
class LockExample
{
private readonly object lockObj = new object();
public void CriticalSection()
{
lock (lockObj)
{
// Critical section
}
}
}