It isn't valid to change a collection in a for each loop. Basically modifying the list in any way invalidates the enumerator. The following is a quote from the documents for IEnumerator:
An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and its behavior is undefined.
For more information take a look at this post. As far as the parallel implementations:
Parallel.ForEach
- This is subject to the same IEnumerator issues that a standard for each is
Parallel.For
- This passes the number of loops into the for as a constant, not as a reference. That means when count changes it doesn't change the number of times it will loop
A safer pattern is to add, remove, and modify your list elements prior to calling the parallels implementation. Then threads can process these elements. If that can't be done, then determine the number of elements that you will have after the loop, then use an array to store/process these by index. Finally pull any non-null values back into a list. That way you don't have to worry about thread safety with regards to your list (Insert
would push other elements forward making your indexes invalid). The following should work:
// 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);
Now you can "add", "remove" and "modify" as much as you want and the result is back in a list!