如何修改队列集合在一个循环? [英] How can I modify a queue collection in a loop?

查看:105
本文介绍了如何修改队列集合在一个循环?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一种情况,我需要尽快得到处理中删除项目队列。
我知道从集合而在循环我不能删除一个项目,但不知道如果事情
可以与枚举器等...



这只是一个基本的例子抛出错误
枚举数被实例化后集合被修改。



有什么建议?非常感谢!



代码如下:

 类节目
{
静态无效的主要()
{

问答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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆