C#面试题更新 2026-08-05

请说明在 C# 中 lock 关键字在编译和运行时是如何实现的,并且讨论在编写多线程代码时可以采用哪些策略来避免死锁。

技术原理方案权衡问题排查C#

考察说明

考查对 C# lock 语句底层机制(Monitor)的理解以及死锁预防的实际经验。

回答思路

  1. 【回答框架 1】lock 语句是 C# 提供的语法糖,其底层是基于 System.Threading.Monitor 类实现的。编译器将 lock 语句转换为 try-finally 结构,在 try 块开始时调用 Monitor.Enter 并传入锁对象,在 finally 块中调用 Monitor.Exit。Monitor.Enter 会尝试获取对象上的互斥锁,如果锁已被其他线程持有,当前线程会阻塞等待。
  2. 【回答框架 2】Monitor 的锁是基于对象头中的同步索引实现的。每个对象都有关联的同步块索引,用于记录锁状态。Monitor.Enter 通过 Interlocked 操作确保原子性。此外,Monitor 支持可重入,同一线程可以多次进入同一锁,并维护一个递归计数。
  3. 【回答框架 3】避免死锁的关键策略包括:按固定顺序获取多个锁,避免循环等待;使用 Monitor.TryEnter 或 SemaphoreSlim.Wait 加超时,避免无限期等待;尽量缩小锁的粒度,减少持锁时间;考虑使用更高层次的同步结构,如 lock 对象的选择,避免锁定公共对象或字符串等。
  4. 【回答框架 4】在代码审查和设计中,应确保所有线程以相同的顺序获取锁,并尽可能使用不可变数据结构或避免嵌套锁。如果必须使用多个锁,可考虑使用超时机制或使用全局锁排序来消除循环。
  5. 【关键点 1】lock 底层基于 Monitor,编译为 try-finally 结构。
  6. 【关键点 2】Monitor.Enter 阻塞获取锁,Monitor.Exit 释放锁。
  7. 【关键点 3】固定顺序获取多个锁可避免死锁。
  8. 【关键点 4】使用 TryEnter 或超时机制可减少死锁风险。
  9. 【易错点 1】lock 不能保证死锁绝对不发生,需要设计上的保证。
  10. 【易错点 2】锁定值类型或字符串可能导致意外行为。
  11. 【易错点 3】持有锁期间执行耗时操作会增加死锁概率。