Запуск фонового потока (ов) миллионы раз - PullRequest
1 голос
/ 05 марта 2012

Я разрабатываю графический интерфейс WPF (.net 3.5) с привязкой BindingList к сетке данных.Я обрабатываю ListChangedEvent из BindingList, и в этом я выполняю дорогой запрос linq для того же BindingList и обновляю определенные свойства в результате linq (, который корректно вызывает и отправляет событие INotifyPropertyChanged.PropertyChanged в графическом интерфейсепри обновлении ).

Мой список привязок может уже содержать миллионы записей, и GUI иногда может выполнять миллионы транзакций (добавлять или удалять) с этими записями в течение нескольких секунд.Таким образом, каждая транзакция будет вызывать событие ListChangedEvent.

Именно на это влиял мой графический интерфейс, поэтому я перенес проверку linq на фоновый поток.Но вот сделка ...

  1. Чтобы получить последовательные результаты, я должен создать новый фоновый поток на каждый ListChangedEvent полученный.Это приведет к тому, что миллионы потоков будут порождаться в памяти за несколько секунд ... очень большое узкое место в памяти.
  2. Я могу использовать один фоновый рабочий, но он не может запустить новую асинхронную работу, пока она не будет завершена, поэтомунам придется ждать в потоке GUI.GUI зависнет, и это снова проблема!
  3. Я могу проверить bgWorker.IsBusy (), но это будет пропустить много ListChangedEvent обработано, пока работник занят, таким образом теряя целостностьзапроса linq.

  4. Предположим, я нашел выход для решения этой проблемы, рассмотренной в пунктах 2 и 3 выше, в любом случае я должен положиться на снимок сбор миллионов записей на каждую работу, которую я выполняю.Это приведет к тому, что большое количество локальных миллионов коллекций записей будет создано и скопировано, возможно, в течение нескольких секунд ...

Так что я запутался, какое решение подойдет мне лучше всего ...

Существующий псевдокод:

  ....
  var bindingList = GetMillionsOfRecordsBindingList();
  bindingList.ListChanged += OnEachItemChanged;
  mydataGrid.ItemsSource = bindingList;      
  .... 

  private void OnEachItemChanged(object sender, ListChangedEventArgs e)
  {
      var currentStauses = GetCurrentStatuses();

      var matchedItems = from p in bindingList
      where p.RefID != null
      and curStauses.Any(status => status.Type == p.Status.Type) 
      select p;

      // We have checked that in production the matchedItems collection
      // less than hundred items updated with their statuses at a time. 

      foreach(var p in matchedItems)
      {
          p.ShowHighlightAnimation = true; //GUI runs animations on the updated items.
      }
  }

Мой предложенный псевдокод:

  private void OnEachItemChanged(object sender, ListChangedEventArgs e)
  {
      var bgWrkr = new BackgroundWorker(); 
      // This will spawn millions of threads
      // (even if they wait in threadpool we have million threads to finish)

      bgWrker.DoWork
         += (o, args) =>
           { 
               var listOfItems = args.Argument as List<MyItems>;
               var currentStauses = GetCurrentStatuses();

               var matchedItems = from p in bindingList
               where p.RefID != null
                     && curStauses.Any(status => status.Type == p.Status.Type) 
               select p;        

               foreach(var p in matchedItems)
               {
                    p.ShowHighlightAnimation = true; 
                    //GUI runs animations on the updated items.
               }
           };

       bgWrkr.RunWorkerAsync(bindingList.ToList());
   }

Я знаю, этот кододинаково плохо .... Следовательно, я запутался в правильном подходе!

Примечание: Я не могу контролировать другой процесс, который выполняет эти миллионы транзакций в списке привязок.Это может быть сделано за несколько секунд или неторопливо за день.Таким образом, синхронизация LINQ (которую я выполняю в другом потоке) только в конце этого процесса исключена.

1 Ответ

1 голос
/ 05 марта 2012

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

Как насчет использования ThreadPool ? Это позволяет использовать множество потоков для выполнения задач. Вы можете отправлять задачи в пул потоков через QueueUserWorkItem . Вам, однако, придется маршалировать обновления обратно в поток пользовательского интерфейса через Dispatcher.BeginInvoke.

...