I am trying to add a batch of nodes to a TreeView asynchronously, in 50ms time intervals, but I am getting an exception that says the collection has been modified. How can I prevent this exception from occurring?
Collection was modified; enumeration operation may not execute.
public partial class Form1 : Form
{
private SynchronizationContext synchronizationContext;
private DateTime previousTime = DateTime.Now;
private List<int> _batch = new List<int>();
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
synchronizationContext = SynchronizationContext.Current;
await Task.Run(() =>
{
for (var i = 0; i <= 5000000; i++)
{
_batch.Add(i);
UpdateUI(i);
}
});
}
public void UpdateUI(int value)
{
var timeNow = DateTime.Now;
if ((DateTime.Now - previousTime).Milliseconds <= 50) return;
synchronizationContext.Post(new SendOrPostCallback(o =>
{
foreach (var item in _batch)
{
TreeNode newNode = new TreeNode($"{item}");
treeView1.Nodes.Add(newNode);
}
}), null);
_batch.Clear();
previousTime = timeNow;
}
}
List<T>is not thread safe. You are trying to modify the list while you are trying to read from it in yourforeach.There are a few ways to solve this.
Use ConcurrentQueue
Change:
To:
You can add items by calling:
Then you can grab them all out and add them in a thread-safe manner:
Use
List, but with Thread-synchronization ObjectsCreate a new object like so:
Then add these helper methods:
This ensures only one thread at a time is operating on the
Listobject.There are other ways to do this as well, but this is the gist of the problem. Your code won't be as fast because of the overhead of synchronizing data, but you won't crash.