如何在循环中修改队列集合?

我有一个场景,我需要在处理后尽快删除队列中的项目。 我知道我不能在循环中从一个集合中删除一个项目但是想知道是否可以用枚举器等完成某些事情……

这只是一个基本的例子,抛出错误“在实例化枚举器后修改了集合”。

有什么建议? 非常感谢!!!

代码如下:

class Program { static void Main() { Queue queueList = GetQueueList(); foreach (Order orderItem in queueList) { Save(orderItem); Console.WriteLine("Id :{0} Name {1} ", orderItem.Id, orderItem.Name); queueList.Dequeue(); } Console.Read(); } private static void Save(Order orderItem) { //we are pretending to save or do something. } private static QueueGetQueueList() { Queue orderQueue = new Queue(); orderQueue.Enqueue(new Order { Id = 1, Name = "Order 1" }); orderQueue.Enqueue(new Order { Id = 1, Name = "Order 2" }); orderQueue.Enqueue(new Order { Id = 2, Name = "Order 3" }); orderQueue.Enqueue(new Order { Id = 3, Name = "Order 4" }); orderQueue.Enqueue(new Order { Id = 4, Name = "Order 5" }); return orderQueue; } } public class Order { public int Id { get; set; } public string Name { get; set; } } 

将你的foreach改为:

 while (queueList.Count > 0) { Order orderItem = queueList.Dequeue(); Save(orderItem); Console.WriteLine("Id :{0} Name {1} ", orderItem.Id, orderItem.Name); } 

编辑:

要重新处理如果保存失败,请执行以下操作:

 while (queueList.Count > 0) { Order orderItem = queueList.Dequeue(); if (!Save(orderItem)) { queueList.Enqueue(orderItem); // Reprocess the failed save, probably want more logic to prevent infinite loop } else { Console.WriteLine("Successfully saved: {0} Name {1} ", orderItem.Id, orderItem.Name); } } 

编辑:

John K提到线程安全,如果您有多个线程访问同一个Queue ,这是一个有效的问题。 有关涉及简单线程安全问题的ThreadSafeQueue类,请参见http://ccutilities.codeplex.com/SourceControl/changeset/view/40529#678487 。


编辑:这是我一直指着大家的线程安全示例:-)

这是一个提到的线程安全问题的例子。 如图所示,默认Queue可以“遗漏”项目,同时仍然减少计数。

更新:更好地代表问题。 我从不向Queue添加空项,但标准Queue.Dequeue()返回多个空值。 仅此一项就可以了,但这样做会从内部集合中删除有效项目并减少Count 。 在这个特定示例中,从Queue.Dequeue()操作返回的每个null项表示从未处理过的有效项,这是一个安全的假设。

 using System; using System.Collections.Generic; using System.Threading; namespace SO_ThreadSafeQueue { class Program { static int _QueueExceptions; static int _QueueNull; static int _QueueProcessed; static int _ThreadSafeQueueExceptions; static int _ThreadSafeQueueNull; static int _ThreadSafeQueueProcessed; static readonly Queue _Queue = new Queue(); static readonly ThreadSafeQueue _ThreadSafeQueue = new ThreadSafeQueue(); static readonly Random _Random = new Random(); const int Expected = 10000000; static void Main() { Console.Clear(); Console.SetCursorPosition(0, 0); Console.WriteLine("Creating queues..."); for (int i = 0; i < Expected; i++) { Guid guid = Guid.NewGuid(); _Queue.Enqueue(guid); _ThreadSafeQueue.Enqueue(guid); } Console.SetCursorPosition(0, 0); Console.WriteLine("Processing queues..."); for (int i = 0; i < 100; i++) { ThreadPool.QueueUserWorkItem(ProcessQueue); ThreadPool.QueueUserWorkItem(ProcessThreadSafeQueue); } int progress = 0; while (_Queue.Count > 0 || _ThreadSafeQueue.Count > 0) { Console.SetCursorPosition(0, 0); switch (progress) { case 0: { Console.WriteLine("Processing queues... |"); progress = 1; break; } case 1: { Console.WriteLine("Processing queues... /"); progress = 2; break; } case 2: { Console.WriteLine("Processing queues... -"); progress = 3; break; } case 3: { Console.WriteLine("Processing queues... \\"); progress = 0; break; } } Thread.Sleep(200); } Console.SetCursorPosition(0, 0); Console.WriteLine("Finished processing queues..."); Console.WriteLine("\r\nQueue Count: {0} Processed: {1, " + Expected.ToString().Length + "} Exceptions: {2,4} Null: {3}", _Queue.Count, _QueueProcessed, _QueueExceptions, _QueueNull); Console.WriteLine("ThreadSafeQueue Count: {0} Processed: {1, " + Expected.ToString().Length + "} Exceptions: {2,4} Null: {3}", _ThreadSafeQueue.Count, _ThreadSafeQueueProcessed, _ThreadSafeQueueExceptions, _ThreadSafeQueueNull); Console.WriteLine("\r\nPress any key..."); Console.ReadKey(); } static void ProcessQueue(object nothing) { while (_Queue.Count > 0) { Guid? currentItem = null; try { currentItem = _Queue.Dequeue(); } catch (Exception) { Interlocked.Increment(ref _QueueExceptions); } if (currentItem != null) { Interlocked.Increment(ref _QueueProcessed); } else { Interlocked.Increment(ref _QueueNull); } Thread.Sleep(_Random.Next(1, 10)); // Simulate different workload times } } static void ProcessThreadSafeQueue(object nothing) { while (_ThreadSafeQueue.Count > 0) { Guid? currentItem = null; try { currentItem = _ThreadSafeQueue.Dequeue(); } catch (Exception) { Interlocked.Increment(ref _ThreadSafeQueueExceptions); } if (currentItem != null) { Interlocked.Increment(ref _ThreadSafeQueueProcessed); } else { Interlocked.Increment(ref _ThreadSafeQueueNull); } Thread.Sleep(_Random.Next(1, 10)); // Simulate different workload times } } ///  /// Represents a thread safe  ///  ///  public class ThreadSafeQueue : Queue { #region Private Fields private readonly object _LockObject = new object(); #endregion #region Public Properties ///  /// Gets the number of elements contained in the  ///  public new int Count { get { int returnValue; lock (_LockObject) { returnValue = base.Count; } return returnValue; } } #endregion #region Public Methods ///  /// Removes all objects from the  ///  public new void Clear() { lock (_LockObject) { base.Clear(); } } ///  /// Removes and returns the object at the beggining of the  ///  ///  public new T Dequeue() { T returnValue; lock (_LockObject) { returnValue = base.Dequeue(); } return returnValue; } ///  /// Adds an object to the end of the  ///  /// The object to add to the  public new void Enqueue(T item) { lock (_LockObject) { base.Enqueue(item); } } ///  /// Set the capacity to the actual number of elements in the , if that number is less than 90 percent of current capactity. ///  public new void TrimExcess() { lock (_LockObject) { base.TrimExcess(); } } #endregion } } } 

foreach是一种在不删除项目时迭代队列的合理方法

当您想要删除和处理项目时,线程安全,正确的方法是一次删除一个并在删除它们后处理它们。

一种方法是这样

 // the non-thread safe way // while (queueList.Count > 0) { Order orderItem = queueList.Dequeue(); Save(orderItem); Console.WriteLine("Id :{0} Name {1} ", orderItem.Id, orderItem.Name); } 

队列中的项目数可能会在queueList.Count和queueList.Dequeue()之间发生变化,因此为了线程安全,您必须使用Dequeue,但是当队列为空时Dequeue将抛出,因此您必须使用exception处理程序。

 // the thread safe way. // while (true) { Order orderItem = NULL; try { orderItem = queueList.Dequeue(); } catch { break; } if (null != OrderItem) { Save(orderItem); Console.WriteLine("Id :{0} Name {1} ", orderItem.Id, orderItem.Name); } } 

对我来说,看起来你正试图逐个处理队列中的元素。

如何在while循环中包装它并处理Dequeue中的每个元素,直到队列为空?