C#中的lock锁
2026/8/26 18:43:38 网站建设 项目流程

lock锁详解


一、先解释:lock是什么?

lock是 C# 中用于"多线程同步"的关键字,它保证同一时刻只有一个线程能进入被锁住的代码块

lock(锁对象){// 临界区:同一时刻只允许一个线程执行这里}

核心概念:

概念说明
锁对象lock括号里的对象,作为"门锁"的标识
临界区lock包裹的代码块,需要互斥保护的代码
互斥一个线程进入后,其他线程必须等待,直到它退出

执行流程:

线程1 到达 lock

锁是否被占用?

线程1 获取锁, 进入临界区

线程1 执行临界区代码

线程1 释放锁

线程2 获取锁, 进入

线程2 阻塞等待


二、为什么需要锁?(问题背景)

多线程并发访问共享数据时,会产生"竞态条件"(Race Condition),导致数据不一致。

通用示例 1:银行账户并发取款(经典问题)
classBankAccount{privateint_balance=1000;// 没有锁:两个线程同时取款会出问题publicvoidWithdraw(intamount){if(_balance>=amount)// ① 检查余额{Thread.Sleep(10);// 模拟耗时(放大竞态窗口)_balance-=amount;// ② 扣款}}}

问题:线程 A 和线程 B 同时取 800 元:

  • A 执行①:_balance=1000 >= 800
  • B 执行①:_balance=1000 >= 800✅(还没被 A 扣)
  • A 执行②:_balance = 200
  • B 执行②:_balance = 200错误!应该扣两次变 -600,或第二次失败)

加锁修复:

classBankAccount{privatereadonlyobject_lock=new();privateint_balance=1000;publicvoidWithdraw(intamount){lock(_lock)// 同一时刻只有一个线程能进来{if(_balance>=amount){Thread.Sleep(10);_balance-=amount;}}}}

现在 A 和 B 会排队执行:A 先扣完(余额 200),B 再检查发现200 < 800,拒绝取款。✅

通用示例 2:多线程累加计数器
intcounter=0;vartasks=newList<Task>();// 1000 个任务同时 counter++for(inti=0;i<1000;i++){tasks.Add(Task.Run(()=>counter++));}Task.WaitAll(tasks.ToArray());Console.WriteLine(counter);// 结果可能不是 1000!(竞态导致丢失更新)

counter++实际是三步:读值 → 加一 → 写回。多线程并发时,两个线程可能同时读到同一个值,各自加一写回,导致只加了一次

加锁修复:

objectlockObj=new();intcounter=0;for(inti=0;i<1000;i++){tasks.Add(Task.Run(()=>{lock(lockObj){counter++;}// 串行化,保证结果 = 1000}));}

三、lock的语法细节

lock(lockObject){// 临界区}

等价于(编译器展开):

Monitor.Enter(lockObject);try{// 临界区}finally{Monitor.Exit(lockObject);// 无论是否异常,都释放锁}

关键点:

  • lock会自动处理异常情况(finally保证释放锁),不会死锁在异常上。
  • 锁对象必须是引用类型objectstring等),不能是值类型。
  • 锁对象通常用private readonly object声明,不对外暴露

四、lock的注意事项与最佳实践

  1. 锁对象要私有:不要用lock(this)lock(typeof(类)),外部可能拿到同一对象造成意外阻塞。
  2. 锁粒度要小:只锁真正需要保护的代码,不要在锁内做耗时操作(如文件 IO、网络请求)。
  3. 不要在锁内调用外部回调:可能死锁。
  4. lock不是万能的:它只保护"代码块",不保护"数据"。所有访问共享数据的代码都必须用同一把锁。
  5. 替代方案ConcurrentDictionaryInterlockedReaderWriterLockSlim等,按需选择。

七、总结

  • lock= 多线程互斥锁,保证同一时刻只有一个线程进入临界区,防止竞态条件。
  • 锁分离设计让业务与日志互不阻塞,提高并发度。
  • 事件广播放在锁外,避免持锁调用外部回调导致死锁。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询