C# 常用的线程同步方式

1、‌**lock 语句(Monitor 类)**‌:

  • 是最常用的同步机制,用于确保同一时间只有一个线程可以访问某个代码块(临界区)。

  • 使用简单,只需指定一个对象作为锁。

    cs 复制代码
    private readonly object _lock = new object();
    public void CriticalSection()
    {
        lock (_lock)
        {
            // 临界区代码
        }
    }

2、‌**Mutex(互斥锁)**‌:

  • 类似于lock,但可以跨进程使用(系统级别的互斥)。
  • 使用WaitOne()获取锁,ReleaseMutex()释放锁。
cs 复制代码
private static Mutex mutex = new Mutex();
public void CriticalSection()
{
    mutex.WaitOne();
    try
    {
        // 临界区代码
    }
    finally
    {
        mutex.ReleaseMutex();
    }
}

3、**Semaphore(信号量)**‌:

  • 控制同时访问某个资源的线程数量,可以指定多个线程同时访问。
  • Semaphore 和 SemaphoreSlim(轻量级,适用于进程内同步)。
cs 复制代码
private static SemaphoreSlim semaphore = new SemaphoreSlim(3); // 允许3个线程同时访问
public async Task CriticalSectionAsync()
{
    await semaphore.WaitAsync();
    try
    {
        // 临界区代码
    }
    finally
    {
        semaphore.Release();
    }

4、ManualResetEvent 和 AutoResetEvent‌:

  • 用于线程间的信号通知。ManualResetEvent需要手动重置,AutoResetEvent在发出信号后自动重置。
cs 复制代码
private static AutoResetEvent resetEvent = new AutoResetEvent(false);
public void Thread1()
{
    // 等待信号
    resetEvent.WaitOne();
    // 执行操作
}
public void Thread2()
{
    // 发送信号
    resetEvent.Set();
}

5、‌ReaderWriterLockSlim‌:

  • 允许多个线程同时读取,但只允许一个线程写入。适用于读多写少的场景。
cs 复制代码
private static ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
public void ReadOperation()
{
    rwLock.EnterReadLock();
    try
    {
        // 读取操作
    }
    finally
    {
        rwLock.ExitReadLock();
    }
}
public void WriteOperation()
{
    rwLock.EnterWriteLock();
    try
    {
        // 写入操作
    }
    finally
    {
        rwLock.ExitWriteLock();
    }
}

6、**SpinLock(自旋锁)**‌:

  • 一种忙等待的锁,适用于等待时间非常短的场景,避免线程切换的开销。
  • 注意:长时间等待会浪费CPU资源。
cs 复制代码
private static SpinLock spinLock = new SpinLock();
public void CriticalSection()
{
    bool lockTaken = false;
    try
    {
        spinLock.Enter(ref lockTaken);
        // 临界区代码
    }
    finally
    {
        if (lockTaken)
            spinLock.Exit();
    }
}

7、Interlocked 类‌:

  • 提供原子操作,用于简单的整数和引用类型的操作(如递增、递减、交换等)。

    cs 复制代码
    private int counter = 0;
    public void IncrementCounter()
    {
        Interlocked.Increment(ref counter);
    }

8、系统自带集合方式

8.1‌**并发集合(System.Collections.Concurrent 命名空间)**‌:

  • 如ConcurrentQueue,ConcurrentDictionary,ConcurrentBag等,它们内部实现了线程安全,可以在多线程环境中直接使用而无需额外的锁。

8.2‌**Immutable Collections(不可变集合)**‌:

  • 不可变集合是线程安全的,因为任何修改都会创建一个新的集合,适用于读多写少且数据变化不频繁的场景。

9、异步编程Task 和 async/await

  • 虽然主要用于异步编程,但通过Task.ContinueWith、Task.WaitAll等也可以实现同步。
  • 结合CancellationToken可以控制任务取消。

10、总结:

根据具体场景选择适当的同步机制:

  • 对于简单的临界区,使用lock即可。
  • 需要控制并发线程数量时,使用Semaphore/SemaphoreSlim。
  • 进程间同步使用Mutex。
  • 读写分离使用ReaderWriterLockSlim。
  • 等待事件通知使用AutoResetEvent/ManualResetEvent。
  • 原子操作使用Interlocked类。
  • 高性能场景且等待时间短时考虑SpinLock。
  • 异步环境可以使用SemaphoreSlim的WaitAsync方法。
相关推荐
ly76894 分钟前
Spring 中的 @Configuration 与 @Component 差异:为何代理时机决定 Bean 生命周期行为
java·后端·spring·注解·代理·bean生命周期
明月_清风15 分钟前
字符串匹配四大经典算法:BF、RK、BM、KMP 到底有什么区别?
后端·算法
疯狂打码的少年15 分钟前
【计算机网络】OSI/RM七层模型(层次结构、各层功能速览)
开发语言·笔记·计算机网络·php
小灰灰搞电子16 分钟前
Rust+Slint 实现ModbusRTU从机调试助手源码分享
开发语言·rust·modbusrtu
程序喵大人17 分钟前
【C++入门】值类别与表达式 - 03 引用绑定:为什么有些参数能接住临时对象
开发语言·c++·引用绑定
卷无止境22 分钟前
测试全绿,功能能跑,代码却烂到没法上线:AI编程助手留下的十个坑
后端·python
子非鱼a27 分钟前
【WEB】[RoarCTF 2019]Easy Java
java·开发语言
软件黑马王子32 分钟前
19.资源加载模块:主要作用和基本原理
开发语言·前端框架·c#
明月_清风43 分钟前
多模式字符串匹配:Trie 与 AC 自动机
后端·算法
嘻哈baby1 小时前
不用 LangChain,从 0 写一个 Ollama 本地 RAG
后端