使用Monitor与Lock实现生产者消费者时遇同步方法调用错误求助
解决生产者-消费者模型中的“Object synchronization method was called from an unsynchronized block of code”错误
兄弟,太懂这种查了一堆帖子还是卡壳的憋屈感了!你遇到的这个错误,本质上是CLR的强制规则:Monitor类的Wait、Pulse或者PulseAll方法,必须在持有对应对象锁的代码块里调用——不然就会直接抛出这个异常。
问题根源拆解
你提到用了Lock和两个Monitor对象,大概率是在调用Monitor.Wait()或者Monitor.Pulse()的时候,没先通过lock语句获取该对象的排他锁。比如错误写法可能是这样:
// 错误示例:调用Monitor.Wait前没lock对应的对象 Monitor.Wait(_producerMonitor);
而正确的姿势必须是先锁死目标对象,再调用它的同步方法:
// 正确示例 lock (_producerMonitor) { Monitor.Wait(_producerMonitor); }
修正后的完整生产者-消费者示例
我给你写一个符合规范的实现,用队列当缓冲区,两个Monitor分别控制生产者和消费者的等待/唤醒逻辑,你可以对照自己的代码调整:
using System; using System.Collections.Generic; using System.Threading; namespace ProducerConsumer { public class ProducerConsumerManager { private readonly Queue<int> _buffer = new Queue<int>(); private readonly object _producerLock = new object(); private readonly object _consumerLock = new object(); private const int MaxBufferSize = 5; // 缓冲区最大容量 public void Produce() { while (true) { lock (_producerLock) { // 用while循环判断缓冲区状态,避免虚假唤醒 while (_buffer.Count >= MaxBufferSize) { Monitor.Wait(_producerLock); // 缓冲区满,生产者等待 } int item = new Random().Next(1, 100); _buffer.Enqueue(item); Console.WriteLine($"Produced: {item}, Buffer size: {_buffer.Count}"); // 唤醒等待的消费者 lock (_consumerLock) { Monitor.Pulse(_consumerLock); } } Thread.Sleep(500); // 模拟生产耗时 } } public void Consume() { while (true) { lock (_consumerLock) { while (_buffer.Count == 0) { Monitor.Wait(_consumerLock); // 缓冲区空,消费者等待 } int item = _buffer.Dequeue(); Console.WriteLine($"Consumed: {item}, Buffer size: {_buffer.Count}"); // 唤醒等待的生产者 lock (_producerLock) { Monitor.Pulse(_producerLock); } } Thread.Sleep(1000); // 模拟消费耗时 } } } class Program { static void Main(string[] args) { var manager = new ProducerConsumerManager(); Thread producerThread = new Thread(manager.Produce); Thread consumerThread = new Thread(manager.Consume); producerThread.Start(); consumerThread.Start(); producerThread.Join(); consumerThread.Join(); } } }
几个关键注意点
- 务必用
while循环判断缓冲区状态,而不是if——因为线程可能被虚假唤醒,唤醒后缓冲区状态可能已经变化了 - 所有
Monitor的同步方法,必须放在对应对象的lock块内部 - 如果你的代码里有跨锁操作,要确保锁的顺序一致,避免死锁
内容的提问来源于stack exchange,提问作者Alex Chiurtu
相关产品推荐
相关产品推荐

