Как добавить ListItem в список во время итерации (C #)? - PullRequest
3 голосов
/ 10 сентября 2011

У меня есть небольшое приложение, которое использует BackgroundWorker для постоянной обработки IEnumerator<T> list.

Код в основном так:

while(true){
    foreach(T item in list){
       // Process each item and send process
       // Add an object in child List ( List<T1> item.Result )
    }
    Thread.Sleep(500);
}

Теперь у меня есть кнопка и текстовое поле, которое будет добавлено непосредственно в IEnumerator.

Проблема в том, что после того, как я добавил кнопку, фоновый работник продолжает обрабатывать текущую обработку элемента, но остановится после завершения этого элемента. Это не продолжается.

Как можно безопасно добавить элемент в список, не влияя на фонового работника? Помимо фонового рабочего также добавьте объекты к элементу. Каким должно быть решение для этого?

Спасибо

Ответы [ 3 ]

6 голосов
/ 10 сентября 2011

Пусть фоновый рабочий перебирает копию исходного списка, а не сам список.

 while (true)
 {
       foreach (T item in new List<T>( list ))
       {
          ....
       }
       Thread.Sleep(500);
 }

Если вы попытаетесь изменить коллекцию при перечислении, перечислитель выдаст исключение. Из документов :

Перечислитель остается действительным, пока остается коллекция без изменений. Если в коллекцию внесены изменения, такие как добавление, изменяя, или удаляя элементы, перечислитель безвозвратно недействительным и следующий вызов MoveNext или Reset бросает InvalidOperationException. Если коллекция изменена между MoveNext и Current, Current возвращает элемент, для которого он установлен, даже если перечислитель уже признан недействительным.

2 голосов
/ 11 сентября 2011

Сначала вы должны изучить основы многопоточного программирования, так что, пошло.

Попробуйте что-нибудь по этому поводу:

// shared queue
ConcurrentQueue<T> queue = new ConcurrentQueue<T>();
// shared wait handle
AutoResetEvent autoEvent = new AutoResetEvent();

Очередь здесь лучше, чем список, потому что она позволяет добавлять и удалять элементы из нее, не беспокоясь об индексе текущего элемента - вы просто Enqueue() элементы для другого и и Dequeue() их для другой. Используйте класс из System.Collections.Concurrent пространства имен, который автоматически обрабатывает потокобезопасный доступ для вас (и из-за сложных причин, по которым вы захотите читать позже, быстрее, чем простой lock()).

Теперь поток переднего плана:

// schedule the work
queue.Enqueue(itemOfWork);
// and wake up our worker
autoEvent.Set();

Волшебная часть здесь - это Set(), вызываемая нашим WaitHandle (да, AutoResetEvent - это реализация WaitHandle). Он пробуждает единственный поток, который ожидал срабатывания события синхронизации, без использования таких уродливых конструкций, как Thread.Sleep(). Вызов Sleep() почти всегда является признаком ошибки в многопоточном коде!

Хорошо, для последней части - рабочий поток. Здесь не так много изменений:

while(true)
{
  // wait for the signal
  autoEvent.WaitOne();
  T item;
  // grab the work item
  queue.TryDequeue(out item);

  // handle the item here;
}
1 голос
/ 10 сентября 2011

Вам, вероятно, нужно использовать ключевое слово "lock", чтобы предотвратить одновременный доступ к общей переменной "list" из обоих мест кода.

http://msdn.microsoft.com/en-us/library/c5kehkcz(v=vs.71).aspx

...