Ich versuche, eine Producer/Consumer-Klasse in C#
mit Monitor
zu implementieren. Die Idee ist, dass der Verbraucher blocken muss, bis der Produzent einen Artikel für den Verbraucher hat, der Produzent aber weiter produzieren soll. Mein Produzent produziert einen Gegenstand und wartet/schläft dann für einige Zeit, bevor er wieder produziert.Warum blockiert mein Produzent manchmal für immer?
Das Problem Ich sah, dass der Produzent nie aus Thread.Sleep(time)
aufwacht. Vielleicht gibt es irgendwo eine Deadlock-Situation.
Bitte helfen Sie mir, dies zu verstehen.
Nur als Anmerkung, ich will nicht BlockingCollection
verwenden ... Hier ist mein Code ...
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;
}
}
Und die Verwendung dieser Klasse ist wie folgt
static void Main(string[] args)
{
ProducerConsumerEx pc = new ProducerConsumerEx();
pc.Start();
while (true)
{
string t = pc.Consume();
Console.WriteLine($"Main {t}");
}
}
Sie die Linien wechseln sollte '_tProducer.Start();' und '_keepProducing = true;', man weiß ja nie wenn der Thread vor '_keepProducing = true;' oder danach gestartet wird. –
Sind Sie sicher, dass 'Console.WriteLine' nach' Thread.Sleep' nicht ausgeführt wird? – slawekwin
@slawekwin Ja, ich bin sicher, Console.WriteLine wird nicht ausgeführt, das ist was ich nicht verstehe ... –