如何修改队列集合在一个循环? [英] How can I modify a queue collection in a loop?
问题描述
我有一种情况,我需要尽快得到处理中删除项目队列。
我知道从集合而在循环我不能删除一个项目,但不知道如果事情
可以与枚举器等...
这只是一个基本的例子抛出错误
枚举数被实例化后集合被修改。
有什么建议?非常感谢!
代码如下:
类节目
{
静态无效的主要()
{
问答LT;排序> queueList = GetQueueList();
的foreach(订购OrderItem的在queueList)
{
保存(OrderItem的);
Console.WriteLine(ID:{0}名称{1},orderItem.Id,orderItem.Name);
queueList.Dequeue();
}
Console.Read();
}
私有静态无效保存(订单OrderItem的)
{
//我们假装保存或做一些事情。
}
私人静态队列<排序> GetQueueList()
{
问答LT;排序> OrderQueue的=新问答LT;排序>();
orderQueue.Enqueue(新订单{ID = 1,名称=订单1});
orderQueue.Enqueue(新订单{ID = 1,名称=订单2});
orderQueue.Enqueue(新订单{ID = 2,名称=3阶});
orderQueue.Enqueue(新订单{ID = 3,名称=订单4});
orderQueue.Enqueue(新订单{ID = 4,名称=订单5});
返回OrderQueue的;
}
}
公共类订单
{
公众诠释标识{搞定;组; }
公共字符串名称{;组; }
}
您的foreach更改为:
而(queueList.Count大于0)
{
阶OrderItem的= queueList.Dequeue() ;
保存(OrderItem的);
Console.WriteLine(ID:{0}名称{1},orderItem.Id,orderItem.Name);
}
编辑:
要重新处理,如果保存失败做这样的事情:
而(queueList.Count大于0)
{
阶OrderItem的= queueList.Dequeue();
如果
{
queueList.Enqueue(OrderItem的)(保存(OrderItem的)!); //重新处理保存失败,可能需要更多的逻辑,以防止无限循环,
}
,否则
{
Console.WriteLine(成功保存:{0}名称{1} ,orderItem.Id,orderItem.Name);
}
}
编辑:
约翰ķ提到线程安全这是一个有效的关注,如果你有多个线程访问相同的队列
。请参见 http://ccutilities.codeplex.com/SourceControl/changeset/view/40529#678487 对于涉及简单的线程安全问题 ThreadSafeQueue
类。
编辑:这是我一直强调大家线程安全的例子: - )
下面是提到的线程安全问题的例子。如图所示,默认的队列
可以小姐的项目,同时还降低了计数。
更新::要更好地代表问题。我从来没有空项添加到队列
但标准 Queue.Dequeue()
返回几个空值。仅此一点就可以了,但这样做的一个有效的项目从内部集合和计数
减小删除。这是一个安全的假设,在这个具体的例子,每一个空
项目从 Queue.Dequeue()
操作返回代表。这是从来没有处理一个有效的项目。
使用系统;
使用System.Collections.Generic;
使用的System.Threading;
命名空间SO_ThreadSafeQueue
{
类节目
{
静态INT _QueueExceptions;
静态INT _QueueNull;
静态INT _QueueProcessed;
静态INT _ThreadSafeQueueExceptions;
静态INT _ThreadSafeQueueNull;
静态INT _ThreadSafeQueueProcessed;
静态只读队列<&的Guid GT?; _Queue =新队列<&的Guid GT;?();
静态只读ThreadSafeQueue<&的Guid GT?; _ThreadSafeQueue =新ThreadSafeQueue<&的Guid GT;?();
静态只读随机_Random =新的随机();
const int的预期值=千万;
静态无效的主要()
{
Console.Clear();
Console.SetCursorPosition(0,0);
Console.WriteLine(创建队列......);
的for(int i = 0; I<预期;我++)
{
的Guid GUID = Guid.NewGuid();
_Queue.Enqueue(GUID);
_ThreadSafeQueue.Enqueue(GUID);
}
Console.SetCursorPosition(0,0);
Console.WriteLine(处理队列......);
的for(int i = 0; I< 100;我++)
{
ThreadPool.QueueUserWorkItem(ProcessQueue);
ThreadPool.QueueUserWorkItem(ProcessThreadSafeQueue);
}
INT进度= 0;
而(_Queue.Count大于0 || _ThreadSafeQueue.Count大于0)
{
Console.SetCursorPosition(0,0);
开关(进度)
{
的情况下0:
{
Console.WriteLine(处理队列... |);
进度= 1;
中断;
}
壳体1:
{
Console.WriteLine(处理队列...... /);
进度= 2;
中断;
}
案例2:
{
Console.WriteLine(处理队列...... - );
进度= 3;
中断;
}
案例3:
{
Console.WriteLine(处理队列...... \\);
进度= 0;
中断;
}
}
的Thread.Sleep(200);
}
Console.SetCursorPosition(0,0);
Console.WriteLine(处理完队列......);
Console.WriteLine(\r\\\
Queue计数:{0}加工:{1,+ Expected.ToString()长度+。}异常:{2,4}空:{3} ,_Queue.Count,_QueueProcessed,_QueueExceptions,_QueueNull);
Console.WriteLine(ThreadSafeQueue计数:{0}加工:{1,+ Expected.ToString()长度+}异常:{2,4}空:{3},_ThreadSafeQueue.Count, _ThreadSafeQueueProcessed,_ThreadSafeQueueExceptions,_ThreadSafeQueueNull);
Console.WriteLine(\r\\\
Press任意键...);
Console.ReadKey();
}
静态无效ProcessQueue(对象没有)
{
,而(_Queue.Count大于0)
{
的Guid? CURRENTITEM = NULL;
试
{
CURRENTITEM = _Queue.Dequeue();
}
赶上(例外)
{
Interlocked.Increment(REF _QueueExceptions);
}
如果(CURRENTITEM!= NULL)
{
Interlocked.Increment(REF _QueueProcessed);
}
,否则
{
Interlocked.Increment(REF _QueueNull);
}
Thread.sleep代码(_Random.Next(1,10)); //模拟不同的工作负载时间
}
}
静态无效ProcessThreadSafeQueue(对象没有)
{
,而(_ThreadSafeQueue.Count大于0)
{
的Guid? CURRENTITEM = NULL;
试
{
CURRENTITEM = _ThreadSafeQueue.Dequeue();
}
赶上(例外)
{
Interlocked.Increment(REF _ThreadSafeQueueExceptions);
}
如果(CURRENTITEM!= NULL)
{
Interlocked.Increment(REF _ThreadSafeQueueProcessed);
}
,否则
{
Interlocked.Increment(REF _ThreadSafeQueueNull);
}
Thread.sleep代码(_Random.Next(1,10)); //模拟不同的工作负载时间
}
}
///<总结>
///表示一个线程安全<见CREF =队列【T】/>
///< /总结>
///< typeparam NAME =T>< / typeparam>
公共类ThreadSafeQueue< T> :队列< T>
{
#地区的私有字段
私人只读对象_LockObject =新的对象();
#endregion
#地区的公共属性
///<总结>
///获取包含在<元件的数量;见CREF =ThreadSafeQueue横置/>
///< /总结>
公开的新诠释计数
{
得到
{
INT的returnValue;
锁(_LockObject)
{
=的returnValue base.Count;
}
返回的returnValue;
}
}
#endregion
#地区的公共方法
///<总结>
///移除的<所有对象;见CREF =ThreadSafeQueue【T】/>
///< /总结>
公开新无效清除()
{
锁(_LockObject)
{
base.Clear();
}
}
///<总结>
///移除并在在<的一开始就返回对象;见CREF =ThreadSafeQueue【T】/>
///< /总结>
///<&回报GT;< /回报>
公共新款T出列()
{$ B $(B T)的returnValue;
锁(_LockObject)
{
=的returnValue base.Dequeue();
}
返回的returnValue;
}
///<总结>
///将对象添加到的<年底;见CREF =ThreadSafeQueue【T】/>
///< /总结>
///&下; PARAM NAME =项>将要添加的对象的<见CREF =ThreadSafeQueue横置/>&下; /参数>
公开新无效排队(T项目)
{
锁(_LockObject)
{
base.Enqueue(项目);
}
}
///<总结>
///容量设置为在<元件的实际数目;见CREF =ThreadSafeQueue【T}/>中,如果该数小于90%的电流capactity的。
///< /总结>
公开新无效TrimExcess()
{
锁(_LockObject)
{
base.TrimExcess();
}
}
#endregion
}
}
}
I have a scenario where I need to remove an item for the queue as soon as been processed. I understand I cannot remove an item from a collection whilst in loop but was wondering if something could be done with the Enumerator etc...
This is just a basic example throwing an error "Collection was modified after the enumerator was instantiated."
Any suggestions? Thanks a lot!!!
Code is as follows:
class Program
{
static void Main()
{
Queue<Order> 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 Queue<Order>GetQueueList()
{
Queue<Order> orderQueue = new Queue<Order>();
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; }
}
Change your foreach to:
while (queueList.Count > 0)
{
Order orderItem = queueList.Dequeue();
Save(orderItem);
Console.WriteLine("Id :{0} Name {1} ", orderItem.Id, orderItem.Name);
}
Edit:
To reprocess if save fails do something like:
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);
}
}
Edit:
John K mentions thread safety which is a valid concern if you have multiple threads accessing the same Queue
. See http://ccutilities.codeplex.com/SourceControl/changeset/view/40529#678487 for a ThreadSafeQueue
class that covers simple thread safety issues.
Edit: Here's the thread safety example I keep pointing everyone to :-)
Here's an example of the thread safety issues mentioned. As shown the default Queue
can "miss" items while still decreasing the count.
Updated: To better represent the problem. I never add a null item to the Queue
but the standard Queue.Dequeue()
returns several null values. This alone would be fine but in doing so a valid item is removed from the internal collection and the Count
decreases. It's a safe assumption, in this specific example, that every null
item returned from a Queue.Dequeue()
operation represents a valid item that was never processed.
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<Guid?> _Queue = new Queue<Guid?>();
static readonly ThreadSafeQueue<Guid?> _ThreadSafeQueue = new ThreadSafeQueue<Guid?>();
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
}
}
/// <summary>
/// Represents a thread safe <see cref="Queue{T}"/>
/// </summary>
/// <typeparam name="T"></typeparam>
public class ThreadSafeQueue<T> : Queue<T>
{
#region Private Fields
private readonly object _LockObject = new object();
#endregion
#region Public Properties
/// <summary>
/// Gets the number of elements contained in the <see cref="ThreadSafeQueue{T}"/>
/// </summary>
public new int Count
{
get
{
int returnValue;
lock (_LockObject)
{
returnValue = base.Count;
}
return returnValue;
}
}
#endregion
#region Public Methods
/// <summary>
/// Removes all objects from the <see cref="ThreadSafeQueue{T}"/>
/// </summary>
public new void Clear()
{
lock (_LockObject)
{
base.Clear();
}
}
/// <summary>
/// Removes and returns the object at the beggining of the <see cref="ThreadSafeQueue{T}"/>
/// </summary>
/// <returns></returns>
public new T Dequeue()
{
T returnValue;
lock (_LockObject)
{
returnValue = base.Dequeue();
}
return returnValue;
}
/// <summary>
/// Adds an object to the end of the <see cref="ThreadSafeQueue{T}"/>
/// </summary>
/// <param name="item">The object to add to the <see cref="ThreadSafeQueue{T}"/></param>
public new void Enqueue(T item)
{
lock (_LockObject)
{
base.Enqueue(item);
}
}
/// <summary>
/// Set the capacity to the actual number of elements in the <see cref="ThreadSafeQueue{T}"/>, if that number is less than 90 percent of current capactity.
/// </summary>
public new void TrimExcess()
{
lock (_LockObject)
{
base.TrimExcess();
}
}
#endregion
}
}
}
这篇关于如何修改队列集合在一个循环?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!