在 C# 中,Monitor.Wait、Monitor.Pulse 和 Monitor.PulseAll 各自的作用是什么?请说明它们的适用场景和使用注意事项。
考察说明
考察对 C# 中 Monitor 类同步原语的理解,特别是 Wait、Pulse、PulseAll 的使用方式及线程同步机制。
回答思路
- 【回答框架 1】Monitor 是 C# 中用于线程同步的类,通过 Enter 和 Exit 实现互斥锁。Wait、Pulse、PulseAll 用于线程间的信号通信,必须在持有锁的代码块内调用,否则会抛出 SynchronizationLockException。
- 【回答框架 2】Wait 释放锁并阻塞当前线程,直到其他线程调用 Pulse 或 PulseAll 通知它重新获取锁。它支持超时和取消等待,返回布尔值表示是否在超时前收到信号。
- 【回答框架 3】Pulse 通知等待队列中的下一个线程(至少一个),但不释放锁,被通知的线程要等当前线程释放锁后才能继续。PulseAll 通知所有等待线程,但只有一个能获取锁,其余继续等待。
- 【回答框架 4】适用场景:生产者-消费者模型中,生产者使用 Pulse 通知消费者有数据;多消费者时用 PulseAll 广播状态变化。注意 Wait 在循环中调用,防止虚假唤醒或忽略信号。
- 【关键点 1】Monitor.Wait 释放锁并阻塞,直到收到 Pulse 或 PulseAll 信号并重新获取锁。
- 【关键点 2】Pulse 唤醒一个等待线程,PulseAll 唤醒所有等待线程,但锁仍由当前线程持有。
- 【关键点 3】调用 Wait/Pulse/PulseAll 必须持有 Monitor 锁。
- 【易错点 1】在调用 Wait 时未持有锁会导致 SynchronizationLockException。
- 【易错点 2】Pulse 不会立即让被唤醒线程执行,需当前线程释放锁,且不保证被唤醒线程能获取锁。
- 【易错点 3】Wait 可能发生虚假唤醒,需在循环条件中重新检测状态。