C#一旦主线程hibernate,所有线程都停止了

我有一个运行Producer-Consumer模型的类,如下所示:

public class SyncEvents { public bool waiting; public SyncEvents() { waiting = true; } } public class Producer { private readonly Queue _queue; private SyncEvents _sync; private Object _waitAck; public Producer(Queue 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 _queue; private SyncEvents _sync; private int count = 0; public Consumer(Queue 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); } } } } ///  /// Act as a consumer to the queries produced by the DataGridViewCustomCell ///  public class QueryThread { private SyncEvents _syncEvents = new SyncEvents(); private Object waitAck = new Object(); private Queue _queryQueue = new Queue(); 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()函数中运行,所有线程都停止,包括生产者和消费者线程。

为什么会发生这种情况,我该如何解决? 谢谢!

所有线程似乎都不太可能实际停止,尽管我应该指出,为了避免错误唤醒,你应该有一个while循环而不是if语句:

 lock (waitAck) { while(queryProducer.secondQueue.Count > 0) { Monitor.Wait(waitAck); } } 

您正在调用Monitor.Wait的事实意味着应该释放waitAck因此它不应该阻止使用者线程锁定…

您能否提供有关生产者/消费者线程“停止”的方式的更多信息? 看起来他们刚刚陷入僵局吗?

您的制作人是否使用NotifyNotifyAll ? 你现在有一个额外的等待线程,所以如果你只使用Notify它只会释放一个线程……如果没有你的ProducerConsumer类的细节,很难看出这是否是一个问题。

如果您可以展示一个简短但完整的程序来演示问题,那将有所帮助。

编辑:好的,现在你已经发布了我可以看到许多问题的代码:

  • 拥有如此众多的公共变量是一种灾难。 您的类应该封装它们的function,以便其他代码不必为了实现零碎而烦恼。 (例如,这里的调用代码实际上不应该访问队列。)

  • 您将项目直接添加到第二个队列,这意味着您无法有效地唤醒生产者将其添加到第一个队列。 为什么你甚至有多个队列?

  • 你总是在生产者线程中等待_sync …为什么? 什么会开始通知它? 一般来说,生产者线程不应该等待,除非你有一个有界缓冲区

  • 您有一个静态变量(_waitAck),每次创建新实例时都会被覆盖。 这是一个坏主意。

你还没有展示你的SyncEvents类 – 是否意味着做一些有趣的事情?

说实话,你似乎有一个很奇怪的设计 – 你可能最好从头开始。 尝试将整个生产者/消费者队列封装在一个具有ProduceConsume方法的类中,以及WaitForEmpty (或类似的东西)。 我认为你会发现同步逻辑更容易。

以下是我对您的代码的看法:

 public class ProducerConsumer { private ManualResetEvent _ready; private Queue _queue; private Thread _consumerService; private static Object _sync = new Object(); public ProducerConsumer(Queue 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() // 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); } } 

我不确定你试图用等待函数实现什么……我假设你试图对可以排队的项目数量设置某种类型的限制。 在这种情况下,当队列中有太多项目时,只需抛出exception或返回失败信号,调用Enqueue的客户端将继续重试,直到队列可以占用更多项目。 采取乐观的方法将为您节省很多麻烦,它只是帮助您摆脱许多复杂的逻辑。

如果你真的想在那里等待,那么我可以帮你找出更好的方法。 让我知道你想要通过等待实现什么,我会帮助你。

注意:我从我的一个项目中获取此代码,稍微修改它并在此处发布…可能存在一些轻微的语法错误,但逻辑应该是正确的。

更新:根据你的评论我做了一些修改:我在类中添加了另一个ManualResetEvent ,所以当你调用BlockQueue()它会给你一个等待的事件并设置一个标志来阻止Enqueue函数排队更多的元素。 一旦队列中的所有查询都被服务,该标志就被设置为true并且设置了_wait事件,所以无论谁在等待它都会得到信号。

 public class ProducerConsumer { private bool _canEnqueue; private ManualResetEvent _ready; private Queue _queue; private Thread _consumerService; private static Object _sync = new Object(); private static ManualResetEvent _wait = new ManualResetEvent(false); public ProducerConsumer() { lock (_sync) { _queue = new Queue _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); } }