在"for each循环"中修改集合是不被允许的。基本上,以任何方式修改列表都会使枚举器无效。以下是来自IEnumerator文档的引用:
“只要集合保持不变,枚举器就保持有效。如果对集合进行更改,例如添加、修改或删除元素,则枚举器将不可恢复地失效,并且其行为未定义。”
有关更多信息,请参阅
本帖。至于并行实现:
- Parallel.ForEach - 这受到与标准for each相同的IEnumerator问题的影响。
- Parallel.For - 这将循环次数作为常量而非引用传递。这意味着当数量发生变化时,它不会改变循环的次数。
更安全的模式是在调用并行实现之前添加、删除和修改列表元素。然后线程可以处理这些元素。如果无法完成此操作,则确定循环后将拥有的元素数量,然后使用数组按索引存储/处理这些元素。最后将任何非空值拉回到列表中。这样您就不必担心与列表相关的线程安全性(
Insert
会推动其他元素向前,使您的索引无效)。以下内容应该有效:
// EX: might be initialized with a call to the database: "COUNT(id)"
int expectedElements = 10;
if (myList.Count < expectedElements)
for (var idx = myList.Count; idx <= expectedElements; idx++) myList.Add(null);
var elements = myList.ToArray();
System.Threading.Tasks.Parallel.For(0, expectedElements, (idx) =>
{
// "remove" the element
if (idx % 3 == 0) elements[idx] = null;
// "modify" the element
if (idx % 3 == 1) elements[idx] = DifferentElement(idx);
// "add" an element
if (idx % 3 == 2) elements[idx] = GetNewElement(idx);
});
// clear current list, add new elements, remove null values
myList.Clear();
myList.AddRange(elements);
myList.RemoveAll(item => item == null);
现在您可以随意“添加”、“删除”和“修改”,结果将返回到列表中!