为什么这条消费者-生产者的线被冻结了

本文关键字:冻结 结了 生产者 消费者 为什么 | 更新日期: 2023-09-27 18:09:06

我制作了这个生产者-消费者样本,但我不知道为什么它最后会冻结。问题在哪里?如果我在setNum(-99)行设置一个断点;休息后继续,就完成了。也请告诉我这个代码是ok的和线程安全的。它必须这样工作,所以在消费者处理其给定值的同时,必须忽略来自生产者的所有其他值。我对多线程很陌生。

class Program
{
    delegate void SetNumberDelegate(int number);
    static void Main(string[] args)
    {
        Random rnd = new Random();
        ConsumerClass consumerClass = new ConsumerClass();
        SetNumberDelegate setNum = new SetNumberDelegate(consumerClass.setNumber);
        Thread.Sleep(20);
        int num;
        int count = 0;
        Console.WriteLine("Start");
        while (count++ < 100)
        {
            num = rnd.Next(0, 100);
            Console.WriteLine("Generated number {0}", num);
            if (num > 30)
            {
                setNum(num);
            }
        }
        setNum(-99);
        Console.WriteLine("End");
        Console.ReadKey();
    }
}
class ConsumerClass : IDisposable
{
    private int number;
    private object locker = new object();
    private EventWaitHandle _wh = new AutoResetEvent(false);
    private Thread _consumerThread;
    public ConsumerClass()
    {
        number = -1;
        _consumerThread = new Thread(consumeNumbers);
        _consumerThread.Start();
    }
    public void Dispose()
    {
        setNumber(-99);
        _consumerThread.Join();         
        _wh.Close();
    }
    public void setNumber(int num)
    {
        if (Monitor.TryEnter(locker))
        {
            try
            {
                number = num;
                Console.WriteLine("Setting number {0}", number);
            }
            finally
            {
                // Ensure that the lock is released.
                Monitor.Exit(locker);
            }
            _wh.Set();
        }
    }
    public void consumeNumbers()
    {
        while (true)
        {
            Monitor.Enter(locker);
            if (number > -1)
            {
                try
                {
                    Console.WriteLine("Processing number:{0}", number);
                    // simulate some work with number e.g. computing and storing to db
                    Thread.Sleep(20);
                    Console.WriteLine("Done");
                    number = -1;
                }
                finally
                {
                    Monitor.Exit(locker);
                }
            }
            else
            {
                if (number == -99)
                {
                    Console.WriteLine("Consumer thread exit");
                    return;
                }
                Monitor.Exit(locker);
                _wh.WaitOne();         // No more tasks - wait for a signal
            }
        }
    }
}

为什么这条消费者-生产者的线被冻结了

像这样重写setNumber来查看问题:

public void setNumber(int num) {
    if (Monitor.TryEnter(locker)) {
        // etc..
    }
    else Console.WriteLine("Number {0} will never make it to the consumer", num);
}

你必须阻塞,等待消费者准备好消费或使用队列。

Monitor.TryEnter(locker);通常会失败(包括-99),因此您不会设置相当数量的值,这就是设置语句中缺少输出的原因。这是因为它不会等待获取锁,它只会返回false。

问题似乎出现在代码的最后一部分。当你执行this时,你持有锁:

        else
        {
            if (number == -99)
            {
                Console.WriteLine("Consumer thread exit");
                return;
            }
            Monitor.Exit(locker);
            _wh.WaitOne();         // No more tasks - wait for a signal
        }

如果是number == 99,方法返回而不释放锁。

你的ConsumeNumbers方法过于复杂。你可以简化它:

while (true)
{
    _wh.WaitOne();
    lock (locker)
    {
        if (number == -99)
            break;
        if (number > -1)
        {
            // process the number.
            number = -1;
        }
    }
}

将做同样的事情,并且是更简单的代码。顺便说一下,结构:

lock (locker)
{
    // do stuff here
}

等于:

Monitor.Enter(locker);
try
{
    // do stuff here
}
finally
{
    Monitor.Exit(locker);
}