请解释 C# 中 lock 关键字实现线程同步的底层机制是怎样的?
考察说明
考查对 C# 中 lock 关键字实现线程同步底层原理的理解。
回答思路
- 【回答框架 1】lock 是 C# 提供的语法糖,其底层通过 Monitor 类实现。编译器将 lock 语句转换为 try-finally 块,并在 try 块入口调用 Monitor.Enter,在 finally 块中调用 Monitor.Exit,确保锁被正确释放,即使发生异常也能释放锁。
- 【回答框架 2】Monitor.Enter 会尝试获取对象上的互斥锁。若锁未被占用,则当前线程获得锁;若已被占用,则当前线程阻塞,直到锁被释放。Monitor 还支持可重入,即同一线程可以多次获取同一把锁,每次进入时计数器加一,每退出一次计数器减一,只有计数器归零时才真正释放锁给其他线程。
- 【回答框架 3】锁的底层依赖于操作系统原语,如 Windows 上的关键段或更高效的混合锁机制。Monitor 在获取锁时先进行用户态的自旋等待,若自旋超时则切换到内核态阻塞线程,以减少上下文切换开销。C# 中 lock 的对象可以是任意引用类型,但通常使用专门的锁对象,避免对公共对象或 this 加锁。
- 【回答框架 4】lock 只保证互斥访问,不保证业务幂等性。要保证幂等,还需唯一标识、状态记录、唯一约束或去重等额外机制。线程池场景下,lock 不会阻塞线程池线程,而是阻塞调用线程。
- 【关键点 1】lock 通过 Monitor.Enter 和 Monitor.Exit 实现互斥。
- 【关键点 2】Monitor 支持可重入和计数器机制。
- 【关键点 3】底层涉及用户态自旋和内核态阻塞。
- 【关键点 4】应避免对公共对象或 this 加锁。
- 【关键点 5】lock 只保证互斥,不保证幂等。
- 【易错点 1】不能直接说 lock 保证业务幂等,需额外机制。
- 【易错点 2】避免对字符串、公共对象或 this 加锁。
- 【易错点 3】不要混淆 lock 与信号量或读写锁的语义。