Я пытаюсь реализовать класс производителя/потребителя в C#
с использованием Monitor
. Идеи состоят в том, что потребитель должен блокировать до тех пор, пока у производителя не будет товар для потребителя, но производитель должен продолжать производить. Мой продюсер производит какой-то предмет, а затем просто ждет/спит когда-нибудь, прежде чем производить снова.Почему мой продюсер иногда блокируется?
Проблема, которую я видел, что производитель никогда не просыпается от Thread.Sleep(time)
. Возможно, где-то есть тупиковая ситуация.
Пожалуйста, помогите мне понять это.
Так же, как примечание, я не хочу использовать BlockingCollection
... Вот мой код ...
public class ProducerConsumerEx
{
private object _objLocker = new object();
private Thread _tProducer;
private Queue<string> _producerQueue;
private bool _keepProducing;
public ProducerConsumerEx()
{
_keepProducing = false;
_producerQueue = new Queue<string>();
_tProducer = new Thread(Produce);
_tProducer.IsBackground = true;
}
private void Produce()
{
while (_keepProducing)
{
Console.WriteLine($"PRODUCER {Thread.CurrentThread.ManagedThreadId} LOOP");
lock (_objLocker)
{
string item = DateTime.Now.ToString("HH:mm:ss");
_producerQueue.Enqueue(item);
Console.WriteLine($"PRODUCER {DateTime.Now.ToString("HH:mm:ss")} Thread {Thread.CurrentThread.ManagedThreadId} Inserted {item}");
Monitor.Pulse(_objLocker);
Console.WriteLine($"PRODUCER {DateTime.Now.ToString("HH:mm:ss")} Thread {Thread.CurrentThread.ManagedThreadId} AF Pulse {item}");
}
Console.WriteLine($"PRODUCER {DateTime.Now.ToString("HH:mm:ss")} Thread {Thread.CurrentThread.ManagedThreadId} BF Sleep");
Thread.Sleep(10000);
Console.WriteLine($"PRODUCER {DateTime.Now.ToString("HH:mm:ss")} Thread {Thread.CurrentThread.ManagedThreadId} AF Sleep");
}
}
public void Start()
{
if (!_keepProducing)
{
_tProducer.Start();
_keepProducing = true;
}
}
public string Consume()
{
string val = default(string);
Console.WriteLine($"CONSUMER {DateTime.Now.ToString("HH:mm:ss")} Thread {Thread.CurrentThread.ManagedThreadId} BF Consume");
lock (_objLocker)
{
Console.WriteLine($"CONSUMER {DateTime.Now.ToString("HH:mm:ss")} Thread {Thread.CurrentThread.ManagedThreadId} BF Consume Inside");
if (_producerQueue.Count > 0)
{
val = _producerQueue.Dequeue();
}
else
{
Console.WriteLine($"CONSUMER {DateTime.Now.ToString("HH:mm:ss")} Thread {Thread.CurrentThread.ManagedThreadId} WAITING");
Monitor.Wait(_objLocker);
//
if (_producerQueue.Count > 0)
{
val = _producerQueue.Dequeue();
}
}
}
return val;
}
}
И использование этого класса, как это
static void Main(string[] args)
{
ProducerConsumerEx pc = new ProducerConsumerEx();
pc.Start();
while (true)
{
string t = pc.Consume();
Console.WriteLine($"Main {t}");
}
}
Вы должны переключить строки '_tProducer.Start()' и '_keepProducing = истина;', вы никогда не знаете, если поток запускается до '_keepProducing = true; 'или после. –
Вы уверены, что 'Console.WriteLine' после' Thread.Sleep' не выполняется? – slawekwin
@slawekwin Да, я уверен, Console.WriteLine не выполняется, это то, что я не понимаю ... –