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