C产品如何满足特定用户需求?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1047个文字,预计阅读时间需要5分钟。
这是最常见的坑:
正确姿势是:先用 lock(obj) 或 Monitor.Enter 获取锁,再调用 Monitor.Wait;它内部会自动释放该锁,并在被 Pulse 或 PulseAll 唤醒后、重新竞争获取同一把锁——注意,不是立刻恢复执行,而是要再次抢到锁才能继续往下走。
- 错误示例:
Monitor.Wait(someObj)单独写在外面 → 立即崩溃 - 正确结构:
lock (someObj) { Monitor.Wait(someObj); } - 若用
Monitor.Enter手动加锁,必须配对try/finally+Monitor.Exit,否则异常时锁不释放
Monitor.Pulse 只唤醒一个等待线程,且只对“已进入 Wait 队列”的线程有效
Monitor.Pulse 不是广播,也不保证唤醒谁。它只从当前对象的 wait queue 中挑一个线程(通常是等待最久的那个)移到 ready queue,让其有机会重新竞争锁。关键点在于:“Pulse 之前没有 Wait” = 白脉冲,什么也不会发生。
典型误用是生产者先 Pulse、消费者后 Wait,结果消费者永远卡住。因为 Pulse 发生时没人等着,信号丢失了。
- 必须严格遵循“先 Wait,后 Pulse”顺序(或至少确保 Wait 已启动)
- 若需唤醒所有等待者,改用
Monitor.PulseAll,比如多个消费者争抢同一资源 -
Pulse调用本身不要求持有锁?错!它也必须在lock块内,否则同样抛SynchronizationLockException
Wait/Pulse 组合常用于“生产者-消费者”场景,但需配合状态变量判断条件
Monitor.Wait 和 Pulse 本身不传递数据、也不校验业务逻辑。它们只是线程调度开关,真正决定“该不该等”“该不该醒”的,是你自己维护的状态变量(如队列是否为空、缓冲区是否满)。
常见错误是裸调 Wait,不检查条件,导致虚假唤醒(spurious wakeup)或逻辑错乱。.NET 的 Wait 设计允许唤醒后条件仍未满足,所以必须用 while 循环重检。
lock (queueLock) { while (messageQueue.Count == 0) { Monitor.Wait(queueLock); } var msg = messageQueue.Dequeue(); }
- 永远用
while包裹Wait,不用if - 状态变量(如
Count)必须在同一个锁保护下读写,否则有竞态 -
Pulse方通常在修改完状态后立即调用,例如Enqueue后Monitor.Pulse
Wait 可带超时,避免永久阻塞;但超时返回 false 不代表错误,只是“没等到”
Monitor.Wait(object, int) 和 Monitor.Wait(object, TimeSpan) 允许指定最大等待时间。超时后方法返回 false,线程恢复执行,但锁依然被持有(因为 Wait 是在 lock 块里释放并重入的)。
这适合做轮询、防死锁、或实现带截止时间的任务调度。但要注意:返回 false 是正常流程分支,不是异常,不应直接 throw。
- 超时后需手动检查状态是否满足,不满足可 continue 等下一轮,或 break 处理超时逻辑
- 超时值设太小会导致频繁空转,太大则响应延迟高;建议结合业务容忍度设定
- 不要在 Wait 超时后忘记处理未完成的业务路径(比如连接未建立就继续发请求)
本文共计1047个文字,预计阅读时间需要5分钟。
这是最常见的坑:
正确姿势是:先用 lock(obj) 或 Monitor.Enter 获取锁,再调用 Monitor.Wait;它内部会自动释放该锁,并在被 Pulse 或 PulseAll 唤醒后、重新竞争获取同一把锁——注意,不是立刻恢复执行,而是要再次抢到锁才能继续往下走。
- 错误示例:
Monitor.Wait(someObj)单独写在外面 → 立即崩溃 - 正确结构:
lock (someObj) { Monitor.Wait(someObj); } - 若用
Monitor.Enter手动加锁,必须配对try/finally+Monitor.Exit,否则异常时锁不释放
Monitor.Pulse 只唤醒一个等待线程,且只对“已进入 Wait 队列”的线程有效
Monitor.Pulse 不是广播,也不保证唤醒谁。它只从当前对象的 wait queue 中挑一个线程(通常是等待最久的那个)移到 ready queue,让其有机会重新竞争锁。关键点在于:“Pulse 之前没有 Wait” = 白脉冲,什么也不会发生。
典型误用是生产者先 Pulse、消费者后 Wait,结果消费者永远卡住。因为 Pulse 发生时没人等着,信号丢失了。
- 必须严格遵循“先 Wait,后 Pulse”顺序(或至少确保 Wait 已启动)
- 若需唤醒所有等待者,改用
Monitor.PulseAll,比如多个消费者争抢同一资源 -
Pulse调用本身不要求持有锁?错!它也必须在lock块内,否则同样抛SynchronizationLockException
Wait/Pulse 组合常用于“生产者-消费者”场景,但需配合状态变量判断条件
Monitor.Wait 和 Pulse 本身不传递数据、也不校验业务逻辑。它们只是线程调度开关,真正决定“该不该等”“该不该醒”的,是你自己维护的状态变量(如队列是否为空、缓冲区是否满)。
常见错误是裸调 Wait,不检查条件,导致虚假唤醒(spurious wakeup)或逻辑错乱。.NET 的 Wait 设计允许唤醒后条件仍未满足,所以必须用 while 循环重检。
lock (queueLock) { while (messageQueue.Count == 0) { Monitor.Wait(queueLock); } var msg = messageQueue.Dequeue(); }
- 永远用
while包裹Wait,不用if - 状态变量(如
Count)必须在同一个锁保护下读写,否则有竞态 -
Pulse方通常在修改完状态后立即调用,例如Enqueue后Monitor.Pulse
Wait 可带超时,避免永久阻塞;但超时返回 false 不代表错误,只是“没等到”
Monitor.Wait(object, int) 和 Monitor.Wait(object, TimeSpan) 允许指定最大等待时间。超时后方法返回 false,线程恢复执行,但锁依然被持有(因为 Wait 是在 lock 块里释放并重入的)。
这适合做轮询、防死锁、或实现带截止时间的任务调度。但要注意:返回 false 是正常流程分支,不是异常,不应直接 throw。
- 超时后需手动检查状态是否满足,不满足可 continue 等下一轮,或 break 处理超时逻辑
- 超时值设太小会导致频繁空转,太大则响应延迟高;建议结合业务容忍度设定
- 不要在 Wait 超时后忘记处理未完成的业务路径(比如连接未建立就继续发请求)

