C# System.InvalidOperationException:已修改集合
在通过队列枚举时,我遇到以下异常: System.InvalidOperationException: 收集被修改;列举 操作可能无法执行 以下是代码摘录:C# System.InvalidOperationException:已修改集合,c#,collections,thread-safety,C#,Collections,Thread Safety,在通过队列枚举时,我遇到以下异常: System.InvalidOperationException: 收集被修改;列举 操作可能无法执行 以下是代码摘录: 1: private bool extractWriteActions(out List<WriteChannel> channelWrites) 2: { 3: channelWrites = new List<WriteChannel>(); 4: foreach (Tpo
1: private bool extractWriteActions(out List<WriteChannel> channelWrites)
2: {
3: channelWrites = new List<WriteChannel>();
4: foreach (TpotAction action in tpotActionQueue)
5: {
6: if (action is WriteChannel)
7: {
8: channelWrites.Add((WriteChannel)action);
9: lock(tpotActionQueue)
10: {
11: action.Status = RecordStatus.Batched;
12: }
13: }
14: }
15: return (channelWrites.Count > 0);
16: }
1:private bool extractWriteActions(out List channelWrites)
2: {
3:channelWrites=新列表();
4:foreach(tpotActionQueue中的TpotAction操作)
5: {
6:if(操作为写通道)
7: {
8:channelWrites.Add((WriteChannel)操作);
9:锁(tpotActionQueue)
10: {
11:action.Status=RecordStatus.Batched;
12: }
13: }
14: }
15:返回(channelWrites.Count>0);
16: }
我想我理解这个问题——在action.Status=RecordStatus.Batched
处更改哈希表,这会使枚举器上的MoveNext()出错。
问题是,我如何正确地实现这个“模式”?我认为您需要做的就是停止使用foreach,而是将其切换到for循环
for(int i = 0; i < tpotActionQueue.Length; i++)
{
TpotAction action = tpotActionQueue[i];
if (action is WriteChannel)
{
channelWrites.Add((WriteChannel)action);
lock(tpotActionQueue)
{
action.Status = RecordStatus.Batched;
}
}
}
for(int i=0;i
您好,Mike。我想您在迭代TpotationQueue时,一定有其他线程在修改它。由于您仅在for循环中锁定该队列,因此这是可能的。允许您更改集合中某个项的值。您收到的错误意味着项目已被添加或删除,即:集合本身已被修改,而不是集合中的项目。这很可能是由另一个线程向该集合添加或删除项引起的 您应该在方法的开头锁定队列,以防止其他线程在您访问集合时修改集合。或者,您可以在调用此方法之前锁定集合
private bool extractWriteActions(out List<WriteChannel> channelWrites)
{
lock(tpotActionQueue)
{
channelWrites = new List<WriteChannel>();
foreach (TpotAction action in tpotActionQueue)
{
if (action is WriteChannel)
{
channelWrites.Add((WriteChannel)action);
action.Status = RecordStatus.Batched;
}
}
}
return (channelWrites.Count > 0);
}
private bool extractWriteActions(out List channelWrites)
{
锁(tpotActionQueue)
{
channelWrites=新列表();
foreach(tpotActionQueue中的TpotAction操作)
{
if(操作为WriteChannel)
{
channelWrites.Add((WriteChannel)操作);
action.Status=RecordStatus.Batched;
}
}
}
返回(channelWrites.Count>0);
}
我想我在一个集合上使用foreach
循环时也遇到了类似的异常,我试图从集合中删除项目(或者它可能是一个列表,我不记得了)。我最终通过使用for
循环绕过了它。或许可以尝试以下方法:
for (int i=0; i<tpotActionQueue.Count(); i++)
{
TpotAction action = tpotActionQueue.Dequeue();
if (action is WriteChannel)
{
channelWrites.Add((WriteChannel)action);
lock(tpotActionQueue)
{
action.Status = RecordStatus.Batched;
}
}
}
for(int i=0;i您没有tpotActionQueue
的定义,但是如果它只是一个普通的列表
,那么该行不是您的问题。修改集合是添加或删除成员,而不是在包含的对象上设置属性
您有一个锁(tpotActionQueue)
和一个线程安全标签,所以我猜在您枚举时,会有另一个线程从tpotActionQueue
中添加或删除项目。您可能需要同步这些访问。有什么好消息吗
private bool extractWriteActions(out List<WriteChannel> channelWrites)
{
channelWrites= tpotActionQueue.Where<WriteChannel>(x => x is WriteChannel).ToList()
foreach(WriteChannel channel in channelWrites) {
channel.Status = RecordStatus.Batched;
}
return ( channelWrites.Count > 0);
}
private bool extractWriteActions(out List channelWrites)
{
channelWrites=tpotActionQueue.Where(x=>x是WriteChannel.ToList())
foreach(channelWrites中的WriteChannel通道){
channel.Status=RecordStatus.Batched;
}
返回(channelWrites.Count>0);
}
为什么要锁定队列?这个代码对我来说毫无意义。@Kermit\u xc:枚举器文档中更重要的一点是"枚举器没有对集合的独占访问权限;因此,通过集合进行枚举本质上不是线程安全的过程。为保证枚举期间的线程安全,可以在整个枚举期间锁定集合。若要允许多个线程访问集合进行读写,请必须执行你自己的同步。“对。这个代码不做任何模糊的事情。如果你需要在多线程上同步读写器,考虑使用读写器锁。这仍然会在多线程环境中造成问题,看起来TPATActuon队列是一个“全局的”。"变量,另一个线程可能会在调用此方法时对其进行修改。此外,允许您更改集合中的项,因此我看不出这有什么意义。dang!-刚刚验证,在集合上有不同的线程推动元素,但是pops是线程安全的。我有点担心整个迭代的“锁定”性能吗尽管如此。它对时间非常敏感-可能需要完全返工:{。尽管如此!@kermit_xs:“刚刚验证,在集合上有不同的线程推送元素”-这就是你的问题。从“foreach”更改为“for”循环不是您的问题,也不是问题的答案。使用foreach时,您需要在枚举之前锁定集合。枚举数不是线程安全的。是的,但此方法不会删除或向集合中添加项。问题不在此方法范围内。非常感谢,这很有意义…将尝试一下。不,Stan问题在于操作。状态分配,请阅读任何集合的枚举文档。“只要集合保持不变,枚举数就保持有效。如果对集合进行了更改,如添加、修改或删除元素,则枚举数将不可恢复地无效,其行为也未定义”.在这里,他正在修改一个您从未使用过foreach初始化列表的元素?list t=new list();t.Add(new Test());t.Add(new Test());foreach(