You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:30:54