0

J'ai beaucoup utilisé BackgroundWorkers, mais je n'ai jamais rencontré ce problème auparavant. Mon programme analyse la sortie d'un analyseur logique produisant des paquets, dont il existe des milliers. Pour éviter de trop retarder la mise à jour de ListView dans mon formulaire (je signalais précédemment chacun comme il a été trouvé, et le formulaire ne répondait pas), je rassemble les paquets dans BackgroundWorker dans une liste générique (Liste <Packet>) et puis en indiquant que, lorsque n sont trouvés (actuellement 250), ou lorsqu'une exception se produit, ou quand elle se termine.C#: Comment puis-je résoudre "Collection a été modifiée" dans un rappel de rapport d'avancement BackgroundWorker?

Le problème se produit dans mon rappel lorsque je suis itérer sur la liste <Paquet> Je reçois une exception InvalidOperationException, avec l'erreur «Collection was modified». Je ne touche pas à la collection à l'intérieur de foreach (j'ajoute à une autre collection, mais je ne vois pas pourquoi cela pourrait modifier la collection que je suis en train d'itérer - et comment le résoudre ne résout pas le problème.) J'ai même essayé de verrouiller le e.UserState, en stockant le e.UserState dans une portée locale Liste <Packet> et le verrouillage, rien ne semble fonctionner.

est ici le code de ma méthode de rappel:

void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    progressBar.Value = e.ProgressPercentage; 
    packetsListView.SuspendLayout(); 
    lock ((List<Packet>)e.UserState) 
    { 
     foreach (Packet packet in (List<Packet>)e.UserState) 
     { 
      packets.Add(packet); 
      ListViewItem item = new ListViewItem(string.Format("{0}ns", Math.Round(packet.StartSampleNumber * 41.666667))); 
      item.Tag = packet; 
      item.SubItems.Add(new ListViewItem.ListViewSubItem(item, packet.Description)); 
      packetsListView.Items.Add(item); 
     } 
    } 
    packetsListView.ResumeLayout(); 

    statusLabel.Text = string.Format("Analyzing...found {0} {1}", packetsListView.Items.Count, packetsListView.Items.Count == 1 ? "packet" : "packets"); 
} 
+0

Que faites-vous remarquer? >> Je ne touche pas à la collection à l'intérieur du foreach (j'ajoute à une autre collection, mais je ne vois pas pourquoi cela pourrait modifier la collection que je suis en train d'itérer - et commenter ne résout pas le problème.) < < – Les

+0

Est-ce que (List Les

Répondre

5

Une explication simple pour votre problème est que vous utilisez le verrou dans le gestionnaire d'événements ProgressChanged mais pas dans le gestionnaire d'événements DoWork. Qui permet au thread de travail encore de modifier la collection pendant que le thread de l'interface utilisateur l'itère.

Il est simple à résoudre, il suffit de créer un nouveau Liste <> juste après avoir appelé ReportProgress dans le worker. Le thread UI est maintenant le seul qui a une référence à la liste, vous n'avez plus besoin d'utiliser le verrou.

+0

J'ai essayé: Liste <Paquet> nouveau_packets = (Liste <Paquet>) e.UserState; En tant que première ligne de la méthode worker_ProgressChanged(), mais cela n'a pas résolu le problème. – PeterBelm

+0

J'ai relu votre commentaire et j'ai réalisé que j'avais mal compris ce que vous disiez. J'ai essayé d'ajouter la ligne dans DoWork, après l'appel à ReportProgress, et cela l'a corrigé. Merci beaucoup! – PeterBelm

+0

Pourriez-vous s'il vous plaît partager le code de la solution s'il vous plaît? – Cheburek

0

Vous ne pouvez pas modifier une collection sur laquelle vous itérez avec un foreach. Vous appelez la méthode packets.Add à l'intérieur de la boucle foreach et je soupçonne que cette variable packets pointe vers la même collection que vous itérez.

Si ce n'est pas le cas, vous pouvez essayer de verrouillage sur un champ statique privé que vous déclarez dans votre formulaire:

private static object _syncRoot = new object(); 

puis:

lock (_syncRoot) 
{ 
    ... 
} 
+0

Merci pour votre réponse, cependant 'paquets' est déjà une propriété statique privée dans le formulaire, initialisée dans le constructeur du formulaire et la seule autre référence est la ligne packets.Add. Donc, il ne fait absolument pas référence à la même collection. Le verrouillage également ne résout pas le problème. – PeterBelm

0

J'ai trouvé que si la performance n'est pas un gros problème, l'utilisation de la collecte simultanée fonctionne plutôt bien. Plus d'infos here

0

Créez cette méthode avec packet, puis il ne peut pas alias celui que vous avez parcouru.