为什么这个消费者 - 生产者线程被冻结?

时间:2011-09-19 16:08:29

标签: c# multithreading thread-safety delegates

我已经制作了这个生产者 - 消费者样本,但我不知道为什么它最终冻结了。 问题出在哪儿?如果我在行setNum(-99)处设置断点;然后在休息后继续它完成好。 还请告诉我这段代码是否正常且线程安全。它必须像那样工作,所以同时消费者正在处理它的给定值,必须忽略来自生产者的所有其他值。 我是多线程的新手。

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
            }
        }
    }
}

3 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

问题似乎出现在代码的最后部分。执行此操作时,您持有锁:

        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);
}