在C#中的乐观锁和悲观锁

在C#中,乐观锁(Optimistic Locking)和悲观锁(Pessimistic Locking)是两种不同的并发控制策略,用于处理多线程环境下的数据一致性问题。它们的核心区别在于对数据冲突的预期和处理方式。

1. 悲观锁(Pessimistic Locking)

悲观锁假设在多线程环境下,数据冲突是常态,因此在访问共享资源时,会提前锁定资源,防止其他线程的干扰。

特点
  • 提前锁定:在操作数据之前,先获取锁,确保独占访问。
  • 适用场景:适用于写操作频繁、冲突概率较高的场景。
  • 缺点:可能导致锁竞争,降低并发性能。
实现方式

在C#中,悲观锁可以通过lock关键字、Monitor类、Mutex类等实现。

示例
复制代码
public class Counter
{
    private int count = 0;
    private readonly object lockObject = new object();

    public void Increment()
    {
        lock (lockObject) // 悲观锁:提前锁定
        {
            count++;
        }
    }

    public int GetCount()
    {
        lock (lockObject) // 悲观锁:提前锁定
        {
            return count;
        }
    }
}

2. 乐观锁(Optimistic Locking)

乐观锁假设在多线程环境下,数据冲突是少数情况,因此不会提前锁定资源,而是在提交操作时检查数据是否被其他线程修改过。如果数据被修改,则放弃操作或重试。

特点
  • 不提前锁定:在操作数据时不加锁,仅在提交时检查冲突。
  • 适用场景:适用于读操作频繁、写操作较少的场景。
  • 优点:减少锁竞争,提高并发性能。
  • 缺点:需要处理冲突,可能会导致操作失败或重试。
实现方式

乐观锁通常通过版本号(Version Number)或时间戳(Timestamp)来检测数据是否被修改。

示例
复制代码
public class Counter
{
    private int count = 0;
    private int version = 0; // 版本号

    public void Increment()
    {
        int currentVersion;
        int newVersion;
        do
        {
            currentVersion = version; // 获取当前版本号
            newVersion = currentVersion + 1; // 计算新版本号
        } while (Interlocked.CompareExchange(ref version, newVersion, currentVersion) != currentVersion);

        // 如果版本号更新成功,说明没有冲突
        count++;
    }

    public int GetCount()
    {
        return count;
    }
}

3. 对比

特点 悲观锁 乐观锁
锁定时机 提前锁定,操作开始时加锁 不提前锁定,仅在提交时检查冲突
适用场景 写操作频繁,冲突概率高 读操作频繁,写操作较少
并发性能 低(锁竞争) 高(减少锁竞争)
实现方式 lockMonitorMutex 版本号(Version Number)、时间戳(Timestamp)
优点 简单,容易实现 提高性能,减少锁竞争
缺点 锁竞争,性能瓶颈 需要处理冲突,可能重试

4. 使用场景

  • 悲观锁
    • 适用场景:数据库事务中,当更新操作频繁且冲突概率较高时,使用悲观锁可以避免频繁的冲突处理。
    • 示例:银行转账系统,多个线程同时操作同一个账户余额。
  • 乐观锁
    • 适用场景:读操作频繁,写操作较少的场景,如在线购物系统中,商品信息的读取和更新。
    • 示例:电商平台的商品库存更新,读取操作远多于写入操作。

5. 在数据库中的应用

  • 悲观锁

    • 在数据库中,可以通过SELECT FOR UPDATE语句实现悲观锁,锁定记录,防止其他事务修改。

    • 示例:

      复制代码
      SELECT * FROM products WHERE id = 1 FOR UPDATE;
  • 乐观锁

    • 在数据库中,可以通过版本号或时间戳字段实现乐观锁。在更新时,检查版本号是否发生变化。

    • 示例:

      复制代码
      UPDATE products SET stock = stock - 1, version = version + 1 WHERE id = 1 AND version = @currentVersion;

总结

  • 悲观锁:适合写操作频繁、冲突概率高的场景,通过提前锁定资源来避免冲突。
  • 乐观锁:适合读操作频繁、写操作较少的场景,通过版本号或时间戳检测冲突,减少锁竞争,提高并发性能。

在实际开发中,可以根据具体需求选择合适的锁机制。

相关推荐
我真会写代码4 小时前
深度解析并发编程锁升级:从偏向锁到重量级锁,底层原理+面试考点全拆解
java·并发编程·
脑电信号要分类6 小时前
将多张图片拼接成一个pdf文件输出
pdf·c#·apache
njsgcs7 小时前
c# solidworks 折弯系数检查
开发语言·c#
格林威8 小时前
工业相机图像采集:Grab Timeout 设置建议——拒绝“假死”与“丢帧”的黄金法则
开发语言·人工智能·数码相机·计算机视觉·c#·机器视觉·工业相机
唐青枫8 小时前
C#.NET SignalR + Redis Backplane 深入解析:多节点部署与跨实例消息同步
c#·.net
FL162386312921 小时前
[C#][winform]segment-anything分割万物部署onnx模型一键抠图演示
开发语言·c#
love530love1 天前
OpenClaw 手机直连配置全流程
人工智能·windows·python·智能手机·c#·agent·openclaw
bcbobo21cn1 天前
C# byte类型和byte数组的使用
开发语言·c#·字节数组·byte类型
月巴月巴白勺合鸟月半1 天前
一次PDF文件的处理(一)
pdf·c#
大鹏说大话1 天前
Java 锁膨胀机制深度解析:从偏向锁到重量级锁的进化之路
开发语言·c#