Почему эта нить потребитель-производитель заморожена? - PullRequest
3 голосов
/ 19 сентября 2011

Я сделал этот образец производителя-потребителя, но я не знаю, почему в конце он зависает. В чем проблема? Если я поставлю точку останова в строке 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 ]

2 голосов
/ 19 сентября 2011

Перепишите setNumber, чтобы увидеть вашу проблему:

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

Вам придется заблокировать, ожидая, пока потребитель будет готов к использованию или к очереди.

0 голосов
/ 19 сентября 2011

Проблема, похоже, в последней части кода. Вы удерживаете блокировку, когда выполняете это:

        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);
}
0 голосов
/ 19 сентября 2011

Monitor.TryEnter(locker); обычно завершится ошибкой (в том числе для -99), поэтому вы не собираетесь устанавливать достаточное количество значений, поэтому в выводе не хватает настроек операторов. Это потому, что он не будет ждать получения блокировки, он просто вернет false.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...