C#线程和队列

这不是关于我能够或应该以最佳方式利用队列的不同方法,而是我所看到的对我来说毫无意义的事情。

void Runner() { // member variable queue = Queue.Synchronized(new Queue()); while (true) { if (0 < queue.Count) { queue.Dequeue(); } } } 

这是在一个线程中运行的:

 var t = new Thread(Runner); t.IsBackground = true; t.Start(); 

其他事件是“排队”其他地方。 我所见过的是在一段时间内,Dequeue实际上会抛出InvalidOperationException,队列为空。 这应该是不可能看到计数如何保证那里有东西,我很肯定没有别的东西是“出队”。

问题:

  1. Enqueue是否有可能在项目完全进入队列之前实际增加计数(无论这意味着什么……)?
  2. 线程是否有可能在Dequeue语句中以某种方式重新启动(到期,重置…),但在它已经删除了一个项目之后立即?

编辑(澄清):

这些代码片段是实现后台帮助程序线程的Wrapper类的一部分。 此处的Dequeue是唯一的Dequeue,并且所有Enqueue / Dequeue都在Synchronized成员变量(队列)上。

使用Reflector,您可以看到不,在添加项目之前,计数不会增加。

正如本指出的那样,看起来确实有多个人叫出队。

你说你是肯定的,没有别的东西叫出列。 是因为你只有一个线程调用dequeue? 是否在任何其他地方都被叫出去?

编辑:

我写了一些示例代码,但无法重现问题。 它只是保持运行和运行,没有任何例外。

在你出错之前它运行了多长时间? 也许你可以分享更多的代码。

 class Program { static Queue q = Queue.Synchronized(new Queue()); static bool running = true; static void Main() { Thread producer1 = new Thread(() => { while (running) { q.Enqueue(Guid.NewGuid()); Thread.Sleep(100); } }); Thread producer2 = new Thread(() => { while (running) { q.Enqueue(Guid.NewGuid()); Thread.Sleep(25); } }); Thread consumer = new Thread(() => { while (running) { if (q.Count > 0) { Guid g = (Guid)q.Dequeue(); Console.Write(g.ToString() + " "); } else { Console.Write(" . "); } Thread.Sleep(1); } }); consumer.IsBackground = true; consumer.Start(); producer1.Start(); producer2.Start(); Console.ReadLine(); running = false; } } 

以下是我认为有问题的序列是:

  1. (0 < queue.Count)计算结果为true,队列不为空。
  2. 此线程被抢占 ,另一个线程运行。
  3. 另一个线程从队列中删除一个项目,清空它。
  4. 此线程恢复执行,但现在位于if块中,并尝试将空列表出列。

但是,你说什么都没有出列......

尝试输出if块内的计数。 如果您看到计数跳跃数字向下,则其他人正在出列。

以下是关于此主题的MSDN页面的可能答案:

枚举集合本质上不是线程安全的过程。 即使集合是同步的,其他线程仍然可以修改集合,这会导致枚举器抛出exception。 为了在枚举期间保证线程安全,您可以在整个枚举期间锁定集合,也可以捕获由其他线程所做的更改导致的exception。

我的猜测是你是对的 – 在某些时候,有一种竞争条件正在发生,你最终会出现一些不存在的东西。

Mutex或Monitor.Lock在这里可能是合适的。

祝好运!

“排队”数据的其他区域是否也使用相同的同步队列对象? 为了使Queue.Synchronized成为线程安全的,所有Enqueue和Dequeue操作都必须使用相同的同步队列对象。

来自MSDN :

为了保证Queue的线程安全,所有操作必须仅通过此包装器完成。

编辑:如果你循环许多涉及繁重计算的项目或者如果你使用长期线程循环(通信等),你应该考虑使用等待函数,如System.Threading.Thread.Sleep , System。 Threading.WaitHandle.WaitOne , System.Threading.WaitHandle.WaitAll ,或循环中的System.Threading.WaitHandle.WaitAny ,否则可能会导致系统性能下降。

问题1:如果你正在使用同步队列,那么:不,你是安全的! 但是您需要在供应商和供料器两侧使用同步实例。

问题2:在没有工作要做的时候终止你的工作线程是一项简单的工作。 但是,无论哪种方式都需要监视线程,或者只要队列有事可做,队列就会启动后台工作线程。 最后一个听起来更像是ActiveObject模式,而不是一个简单的队列(Single-Responsibily-Pattern表示它应该只进行排队)。

另外,我会使用阻塞队列而不是上面的代码。 代码的工作方式需要CPU处理能力,即使没有工作要做。 阻塞队列可以让您的工作线程在没有任何操作时进入hibernate状态。 您可以在不使用CPU处理能力的情况下运行多个hibernate线程。

C#没有阻塞队列实现,但有很多。 请参阅此示例和此示例 。