我有一个像这样运行Producer-Consumer模型的类:
public class SyncEvents
{
public bool waiting;
public SyncEvents()
{
waiting = true;
}
}
public class Producer
{
private readonly Queue<Delegate> _queue;
private SyncEvents _sync;
private Object _waitAck;
public Producer(Queue<Delegate> q, SyncEvents sync, Object obj)
{
_queue = q;
_sync = sync;
_waitAck = obj;
}
public void ThreadRun()
{
lock (_sync)
{
while (true)
{
Monitor.Wait(_sync, 0);
if (_queue.Count > 0)
{
_sync.waiting = false;
}
else
{
_sync.waiting = true;
lock (_waitAck)
{
Monitor.Pulse(_waitAck);
}
}
Monitor.Pulse(_sync);
}
}
}
}
public class Consumer
{
private readonly Queue<Delegate> _queue;
private SyncEvents _sync;
private int count = 0;
public Consumer(Queue<Delegate> q, SyncEvents sync)
{
_queue = q;
_sync = sync;
}
public void ThreadRun()
{
lock (_sync)
{
while (true)
{
while (_queue.Count == 0)
{
Monitor.Wait(_sync);
}
Delegate query = _queue.Dequeue();
query.DynamicInvoke(null);
count++;
Monitor.Pulse(_sync);
}
}
}
}
/// <summary>
/// Act as a consumer to the queries produced by the DataGridViewCustomCell
/// </summary>
public class QueryThread
{
private SyncEvents _syncEvents = new SyncEvents();
private Object waitAck = new Object();
private Queue<Delegate> _queryQueue = new Queue<Delegate>();
Producer queryProducer;
Consumer queryConsumer;
public QueryThread()
{
queryProducer = new Producer(_queryQueue, _syncEvents, waitAck);
queryConsumer = new Consumer(_queryQueue, _syncEvents);
Thread producerThread = new Thread(queryProducer.ThreadRun);
Thread consumerThread = new Thread(queryConsumer.ThreadRun);
producerThread.IsBackground = true;
consumerThread.IsBackground = true;
producerThread.Start();
consumerThread.Start();
}
public bool isQueueEmpty()
{
return _syncEvents.waiting;
}
public void wait()
{
lock (waitAck)
{
while (_queryQueue.Count > 0)
{
Monitor.Wait(waitAck);
}
}
}
public void Enqueue(Delegate item)
{
_queryQueue.Enqueue(item);
}
}
代码运行顺利但是wait()函数。 在某些情况下,我想等到队列中的所有函数都运行完毕,所以我创建了wait()函数。
制作人将在适当的时间发出waitAck脉冲。
然而,当行“Monitor.Wait(waitAck);”时在wait()函数中运行,所有线程都停止,包括生产者和消费者线程。
为什么会发生这种情况,我该如何解决?谢谢!
答案 0 :(得分:1)
所有线程似乎不太可能实际停止,但我应该指出,为了避免错误唤醒,你应该有一个while循环而不是if语句:
lock (waitAck)
{
while(queryProducer.secondQueue.Count > 0)
{
Monitor.Wait(waitAck);
}
}
您正在调用Monitor.Wait
这意味着应该释放waitAck
,因此它不应该阻止使用者线程锁定...
您能否提供有关生产者/消费者线程“停止”的方式的更多信息?看起来他们刚刚陷入僵局吗?
您的制作人是否使用Notify
或NotifyAll
?你现在有一个额外的等待线程,所以如果你只使用Notify
它只会释放一个线程...如果没有{{1}的详细信息,很难看出这是否是一个问题}和Producer
类。
如果你能展示一个简短但完整的程序来证明这个问题,那会有所帮助。
编辑:好的,现在您已经发布了我可以看到许多问题的代码:拥有如此众多的公共变量是一种灾难。你的类应该封装它们的功能,这样其他代码就不必去寻找实现的部分。 (例如,您的调用代码实际上不应该访问队列。)
您将项目直接添加到第二个队列,这意味着您无法有效唤醒生产者将其添加到第一个队列。为什么你甚至有多个队列?
你总是在生产者线程中等待Consumer
...为什么?什么会开始通知它?一般来说,生产者线程不应该等待,除非你有一个有界缓冲区
您有一个静态变量(_waitAck),每次创建新实例时都会被覆盖。这是个坏主意。
你还没有展示你的_sync
课程 - 是否意味着做一些有趣的事情?
老实说,看起来你的设计很奇怪 - 你可能最好从头开始。尝试将整个生产者/消费者队列封装在一个具有SyncEvents
和Produce
方法的类中,以及Consume
(或类似的东西)。我认为你会发现同步逻辑更容易。
答案 1 :(得分:1)
以下是我对您的代码的看法:
public class ProducerConsumer
{
private ManualResetEvent _ready;
private Queue<Delegate> _queue;
private Thread _consumerService;
private static Object _sync = new Object();
public ProducerConsumer(Queue<Delegate> queue)
{
lock (_sync)
{
// Note: I would recommend that you don't even
// bother with taking in a queue. You should be able
// to just instantiate a new Queue<Delegate>()
// and use it when you Enqueue. There is nothing that
// you really need to pass into the constructor.
_queue = queue;
_ready = new ManualResetEvent(false);
_consumerService = new Thread(Run);
_consumerService.IsBackground = true;
_consumerService.Start();
}
}
public override void Enqueue(Delegate value)
{
lock (_sync)
{
_queue.Enqueue(value);
_ready.Set();
}
}
// The consumer blocks until the producer puts something in the queue.
private void Run()
{
Delegate query;
try
{
while (true)
{
_ready.WaitOne();
lock (_sync)
{
if (_queue.Count > 0)
{
query = _queue.Dequeue();
query.DynamicInvoke(null);
}
else
{
_ready.Reset();
continue;
}
}
}
}
catch (ThreadInterruptedException)
{
_queue.Clear();
return;
}
}
protected override void Dispose(bool disposing)
{
lock (_sync)
{
if (_consumerService != null)
{
_consumerService.Interrupt();
}
}
base.Dispose(disposing);
}
}
我不确定你试图通过等待函数实现什么...我假设你试图对可以排队的项目数量设置某种类型的限制。在这种情况下,当队列中有太多项目时,只需抛出异常或返回失败信号,调用Enqueue的客户端将继续重试,直到队列可以占用更多项目。采取乐观的方法将为您节省很多麻烦,它只是帮助您摆脱许多复杂的逻辑。
如果你真的想在那里等待,那么我可以帮你找出更好的方法。让我知道你想要通过等待实现什么,我会帮助你。
注意:我从我的一个项目中获取此代码,稍微修改并在此处发布...可能存在一些轻微的语法错误,但逻辑应该是正确的。
更新:根据您的评论,我做了一些修改:我在课程中添加了另一个ManualResetEvent
,因此当您致电BlockQueue()
时,它会为您提供一个事件,您可以等待并设置一个标志以阻止Enqueue函数排队更多元素。一旦队列中的所有查询都得到了服务,该标志就会设置为true,并且_wait
事件被设置为无论谁在等待它都会得到信号。
public class ProducerConsumer
{
private bool _canEnqueue;
private ManualResetEvent _ready;
private Queue<Delegate> _queue;
private Thread _consumerService;
private static Object _sync = new Object();
private static ManualResetEvent _wait = new ManualResetEvent(false);
public ProducerConsumer()
{
lock (_sync)
{
_queue = new Queue<Delegate> _queue;
_canEnqueue = true;
_ready = new ManualResetEvent(false);
_consumerService = new Thread(Run);
_consumerService.IsBackground = true;
_consumerService.Start();
}
}
public bool Enqueue(Delegate value)
{
lock (_sync)
{
// Don't allow anybody to enqueue
if( _canEnqueue )
{
_queue.Enqueue(value);
_ready.Set();
return true;
}
}
// Whoever is calling Enqueue should try again later.
return false;
}
// The consumer blocks until the producer puts something in the queue.
private void Run()
{
try
{
while (true)
{
// Wait for a query to be enqueued
_ready.WaitOne();
// Process the query
lock (_sync)
{
if (_queue.Count > 0)
{
Delegate query = _queue.Dequeue();
query.DynamicInvoke(null);
}
else
{
_canEnqueue = true;
_ready.Reset();
_wait.Set();
continue;
}
}
}
}
catch (ThreadInterruptedException)
{
_queue.Clear();
return;
}
}
// Block your queue from enqueuing, return null
// if the queue is already empty.
public ManualResetEvent BlockQueue()
{
lock(_sync)
{
if( _queue.Count > 0 )
{
_canEnqueue = false;
_wait.Reset();
}
else
{
// You need to tell the caller that they can't
// block your queue while it's empty. The caller
// should check if the result is null before calling
// WaitOne().
return null;
}
}
return _wait;
}
protected override void Dispose(bool disposing)
{
lock (_sync)
{
if (_consumerService != null)
{
_consumerService.Interrupt();
// Set wait when you're disposing the queue
// so that nobody is left with a lingering wait.
_wait.Set();
}
}
base.Dispose(disposing);
}
}