0

Я использовал BackgroundWorkers совсем немного, но я никогда не испытывал этой проблемы раньше. Моя программа анализирует вывод логического анализатора, производящего пакеты, из которых тысячи. Чтобы предотвратить слишком много задержки, обновляя ListView в моей форме (я ранее сообщал об этом каждый, как было обнаружено, и форма была полностью неактуальной). Я собираю пакеты внутри BackgroundWorker в общем списке (список < Пакет >) и затем сообщают, что либо когда n количество найдено (в настоящее время 250), либо когда происходит исключение, либо когда оно завершено.C#: Как я могу решить, что «Коллекция была изменена» в обратном вызове отчета о ходе работы BackgroundWorker?

Проблема возникает в моем обратном вызове, когда я выполняю перебор по списку < Пакет > Я получаю InvalidOperationException с ошибкой «Коллекция была изменена». Я не трогаю коллекцию внутри foreach (я добавляю в другую коллекцию, но я не вижу причин, по которым это может изменить коллекцию, которую я перебираю, плюс комментирование ее не устраняет проблему.) Я даже попытался заблокировать e.UserState, сохранив e.UserState в локальной области. Список < Пакет > и блокировка, что ничего не работает.

Вот код для моего метода обратного вызова:

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

Что вы комментируете? >> Я не трогаю коллекцию внутри foreach (я добавляю в другую коллекцию, но я не вижу причин, по которым это может изменить коллекцию, которую я повторяю, плюс комментирование ее не устраняет проблему.) < < – Les

+0

Является ли (List Les

ответ

5

Простое объяснение вашей проблемы что вы используете блокировку в обработчике событий ProgressChanged, но не в обработчике событий DoWork. Это позволяет рабочему потоку по-прежнему изменять коллекцию, пока поток пользовательского интерфейса выполняет итерацию.

Просто попробуйте, просто создайте Список <> сразу после вызова ReportProgress в рабочем состоянии. Теперь поток пользовательского интерфейса является единственным, у которого есть ссылка на список, вам больше не нужно использовать блокировку.

+0

Я пробовал: Список < Пакет > new_packets = (Список < Пакет >) e.UserState; Как первая строка метода worker_ProgressChanged(), но это не решило проблему. – PeterBelm

+0

Я перечитал ваш комментарий и понял, что неправильно понял то, что вы говорили. Я попытался добавить строку внутри DoWork после вызова ReportProgress и исправил ее. Большое спасибо! – PeterBelm

+0

Не могли бы вы, пожалуйста, поделиться кодом решения? – Cheburek

0

Вы не можете изменить коллекцию, на которую вы итерацию с Еогеаспом. Вы вызываете метод packets.Add внутри цикла foreach, и я подозреваю, что эта переменная packets указывает на ту же коллекцию, которую вы итерируете.

Если это не так, вы можете попробовать блокировки на частном статическом поле, которое вы объявляете внутри формы:

private static object _syncRoot = new object(); 

, а затем:

lock (_syncRoot) 
{ 
    ... 
} 
+0

Спасибо за ваш ответ, однако «пакеты» уже являются частным статическим свойством в форме, инициализированным в конструкторе форм, и единственной ссылкой на него является строка packages.Add. Поэтому он определенно не относится к одной коллекции. Также блокировка не решает проблему. – PeterBelm

0

Я обнаружил, что если производительность не является большой проблемой, то использование некоторых совместимых сборок работает очень хорошо. Больше информации here

0

Создайте packet в этом методе, а затем он не может быть псевдонимом, пропущенным через.