C#多线程访问资源的实现示例
作者:Winemonk
C#中多线程访问共享资源需要通过同步机制来保证线程安全,通过合理选择同步机制,可以平衡性能与线程安全,下面就来介绍一下如何实现,感兴趣的可以了解一下
在 C# 中,多线程访问共享资源需要通过同步机制来保证线程安全。以下是常见的解决方案及其适用场景:
1.1 锁机制
lock关键字- 基于
Monitor类,确保代码块同一时间仅一个线程进入。 - 适用场景:简单临界区保护。
private readonly object _lockObj = new object(); lock (_lockObj) { // 访问共享资源 }- 基于
Monitor类- 提供更灵活的控制(如超时机制)。
Monitor.Enter(_lockObj); try { /* 操作资源 */ } finally { Monitor.Exit(_lockObj); }Mutex(互斥锁)- 跨进程同步,适用于多进程共享资源。
using var mutex = new Mutex(false, "GlobalMutexName"); mutex.WaitOne(); try { /* 操作资源 */ } finally { mutex.ReleaseMutex(); }SpinLock(自旋锁)- 通过循环等待避免上下文切换,适用于极短临界区。
SpinLock spinLock = new SpinLock(); bool lockTaken = false; spinLock.Enter(ref lockTaken); try { /* 操作资源 */ } finally { if (lockTaken) spinLock.Exit(); }
1.2 信号量机制
Semaphore/SemaphoreSlim- 控制同时访问资源的线程数量。
SemaphoreSlim semaphore = new SemaphoreSlim(3); // 允许3个线程进入 await semaphore.WaitAsync(); try { /* 操作资源 */ } finally { semaphore.Release(); }ReaderWriterLockSlim- 读写分离锁,允许多读单写。
ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(); rwLock.EnterReadLock(); // 读模式 try { /* 读取资源 */ } finally { rwLock.ExitReadLock(); } rwLock.EnterWriteLock(); // 写模式 try { /* 修改资源 */ } finally { rwLock.ExitWriteLock(); }
1.3 事件与信号
ManualResetEvent/AutoResetEvent- 通过信号控制线程阻塞与唤醒。
ManualResetEvent mre = new ManualResetEvent(false); mre.WaitOne(); // 等待信号 mre.Set(); // 发送信号
Barrier(屏障)- 同步多个线程到同一阶段。
Barrier barrier = new Barrier(3); // 等待3个线程到达 barrier.SignalAndWait(); // 每个线程调用此方法
CountdownEvent- 等待指定数量的操作完成。
CountdownEvent cde = new CountdownEvent(3); cde.Signal(); // 每个线程完成后调用 cde.Wait(); // 等待所有完成
1.4 原子操作
Interlocked类- 提供原子操作(如递增、比较交换)。
int value = 0; Interlocked.Increment(ref value); // 原子递增
1.5 线程安全集合
ConcurrentQueue/ConcurrentDictionary等- 内置线程安全的集合,避免手动同步。
var queue = new ConcurrentQueue<int>(); queue.Enqueue(1); if (queue.TryDequeue(out var item)) { /* 处理元素 */ }
1.6 避免共享状态
不可变对象
- 使用
readonly或不可变集合(如ImmutableList),确保数据不可变。
var list = ImmutableList.Create<int>(); list = list.Add(1); // 返回新实例,原数据不变
- 使用
线程本地存储
- 使用
ThreadLocal<T>或[ThreadStatic]为每个线程创建独立副本。
ThreadLocal<int> threadLocal = new ThreadLocal<int>(() => 0); int localValue = threadLocal.Value;
- 使用
1.7 异步与并行
async/await与异步锁- 在异步代码中使用
SemaphoreSlim.WaitAsync()。
private SemaphoreSlim asyncLock = new SemaphoreSlim(1); await asyncLock.WaitAsync(); try { /* 异步操作资源 */ } finally { asyncLock.Release(); }- 在异步代码中使用
TPL (任务并行库)
- 使用
Parallel.For或Task时确保资源安全。
Parallel.For(0, 10, i => { // 需要内部同步机制 });- 使用
1.8 选择策略
- 简单临界区:优先使用
lock或Monitor。 - 读写分离:使用
ReaderWriterLockSlim。 - 高并发读:不可变对象或并发集合。
- 异步场景:
SemaphoreSlim.WaitAsync()。 - 跨进程同步:
Mutex。
通过合理选择同步机制,可以平衡性能与线程安全。
到此这篇关于C#多线程访问资源的实现示例的文章就介绍到这了,更多相关C#多线程访问资源内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
