.net 中并发控制
代码中经常使用多线程的方式来提高应用程序的性能,但当多个线程或进程并发执行同一段代码时,可能会导致竞争条件,死锁,数据不一致等线程安全问题;因此需要使用线程安全的代码或数据结构来避免。.net core 中提供了多种方式来控制并发访问共享资源,实现线程安全。
原子操作
System.Threading中的Interlocked类提供了用于进行原子操作的静态方法,用于确保对共享变量的增减操作是线程安全的,避免了使用锁机制带来的额外的性能开销,可以在高并发的场景下可以减少锁的竞争。
int counter = 0;
public void IncrementCounter()
{
Interlocked.Increment(ref counter); // 线程安全的原子操作
}
锁机制
锁机制是常用的并发控制机制,通过锁定资源,确保同一时刻只有一个线程可以访问特定的代码块或数据结构。.net core 在System.Threading命名空间提供了多种锁机制来实现并发控制。
lock语句(Monitor)
lock语句是调用以 try/finally 块封装的 Monitor.Enter() 和 Monitor.Exit() 方法的语法糖,两者都用于线程同步,确保同一时刻只有一个线程能够进入被锁保护的代码块,其他并发访问的线程会被阻塞,直到当前线程释放锁。
private static readonly object _lock = new object();
public void Increment()
{
lock (_lock)
{
counter++; // 只有一个线程能进入此代码块
}
}
//monitor语句
public void Increment()
{
// 使用 Monitor.Enter 和 Monitor.Exit
for (int i = 0; i < 1000; i++)
{
Monitor.Enter(lockObject); // 锁定对象,进入临界区
try
{
counter++;
}
finally
{
Monitor.Exit(lockObject); // 解锁,使其他线程可以进入临界区
}
}
}
Mutex
Mutex可以进行跨进程的同步机制,确保同一时刻只有一个进程能访问特定资源,适用于多个进程间的同步。
Mutex mutex = new Mutex(false, "MyMutex");
public void SomeMethod()
{
mutex.WaitOne();
try
{
// 执行需要同步的操作
}
finally
{
mutex.ReleaseMutex();
}
}
Semaphore 和SemaphoreSlim
Semaphore和SemaphoreSlim允许最多N个线程同时访问共享资源,超出数量的线程会被阻塞,直到有其他线程释放资源,适用于连接池,数据库池等需要限制并发访问数量的场景。
SemaphoreSlime: 轻量级的实现,专门设计用来提高线程同步的性能,避免了较重的操作系统调用,因此可以用于单一进程内线程同步。
Semaphore: 通过操作系统的内核信号量支持进程间同步,可以在不同的进程中共享信号量,适用于需要跨进程控制并发的场景。
SemaphoreSlim semaphore = new SemaphoreSlim(3, 3);
public async Task SomeMethodAsync()
{
await semaphore.WaitAsync();
try
{
// 执行同步操作
}
finally
{
semaphore.Release();
}
}
SpinLock
SinLock是一种轻量级的锁,通过自旋(不断循环检查锁的状态)来尝试获取锁。自旋过程中,线程不会被挂起,不涉及操作系统的上下文切换,避免了线程挂起和调度的高开销,适用于锁竞争时间比较短的情况。
SpinLock spinLock = new SpinLock();
bool lockTaken = false;
public void SomeMethod()
{
spinLock.Enter(ref lockTaken); // 获取锁
try
{
// 执行同步操作
}
finally
{
if (lockTaken)
spinLock.Exit(); // 释放锁
}
}
ReaderWriterLock和ReaderWriterLockSlim
两种锁允许多个线程同时进行读取,但在写入时,同一时间只有一个线程可以进行,适用于读多写少的场景,如缓存、配置文件等。
两种锁的功能相似,官方文档推荐使用ReaderWriterLockSlim,因为其简化了递归规则以及锁状态的升级降级,并且性能显著优于ReaderWriterLock,具体区别见官方文档。
ReaderWriterLockSlim lockSlim = new ReaderWriterLockSlim();
public void ReadData()
{
lockSlim.EnterReadLock();
try
{
// 执行读取操作
}
finally
{
lockSlim.ExitReadLock();
}
}
public void WriteData()
{
lockSlim.EnterWriteLock();
try
{
// 执行写入操作
}
finally
{
lockSlim.ExitWriteLock();
}
}
线程安全的数据结构
.net core 线程安全的数据结构位于System.Collections.Concurrent命名空间,包括ConcurrentDictionary,ConcurrentQueue,ConcurrentBag
等,提供了线程并发访问控制,避免了直接使用显式锁来保护集合,具体使用方式类似对应的非线程安全的数据结构。
Immutable集合
System.Collections.Immutable命名空间中提供了不可变集合,Immutable集合对象一经创建后不可修改,适用于需要共享和并发访问的数据结构,并且不需要修改数据。包括ImmutableList<T>
,ImmutableQueue<T>
,ImmutabelDictionary
等。
参考内容
Introduction To Locking And Concurrency Control in .NET 6
What is Locking and How to Use a Locking Mechanism in C#
System.Threading
System.Collections.Immutable
System.Collections.Concurrent
ChatGPT
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示