Многопоточный фоновый рабочий дизайн - PullRequest
1 голос
/ 15 ноября 2010

Не имея опыта в многопоточном программировании или технологиях, я хотел бы задать этот вопрос как способ сфокусировать проект на выполнении следующих требований для относительно длительных заданий (от 4 до 10 секунд), которые запускаются в режиме реального времени.время какого-либо действия пользователя:

  1. После того, как задание запущено и до его завершения:
  2. Использовать конструкцию MVVM с состоянием и понятием IsBusy, являющимся связанными свойствами данных в некотором классе модели представления INPC
  3. Тестируемый модуль

Я начал с того, что пытался использовать BackgroundWorker, который является либо подклассами, либо инкапсулирован иным образом, но обнаружил, что меня сбивают с толку, как синхронизировать понятие подсчета истекших секунд в потоке таймера, пока другой поток выполняетРабота.

Идеи дизайна, которые могут привести к более сфокусированным вопросам программирования, высоко ценятся!

Приветствия,
Berryl

Ответы [ 3 ]

2 голосов
/ 15 ноября 2010

Класс BackgroundWorker является отличным инструментом для запуска фоновых задач, но его дизайн иногда заставляет разработчиков идти по неверному пути в отношении того, как должен обновляться пользовательский интерфейс.Проблема в том, что он использует модель push для обновления пользовательского интерфейса.Другими словами, элементы ReportProgress и ProgressChanged предназначены для перенаправления обработчика событий в поток пользовательского интерфейса.Это хорошо для многих ситуаций, но в большинстве других случаях это работает не так хорошо ... как вы заметили.

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

  • Это нарушает тесную связь между пользовательским интерфейсом и рабочими потоками, которые навязывает Control.Invoke.
  • Это возлагает ответственность за обновление потока пользовательского интерфейса на поток пользовательского интерфейса.где он должен принадлежать в любом случае.
  • Поток пользовательского интерфейса должен определять, когда и как часто должно происходить обновление.
  • Нет риска переполнения насоса сообщений пользовательского интерфейса, как в случаес методами маршалинга, инициированными рабочим потоком.
  • Рабочему потоку не нужно ждать подтверждения того, что обновление было выполнено, прежде чем переходить к следующим шагам (т. е. вы получаете большую пропускную способность как для пользовательского интерфейса, так и для пользовательского интерфейса).рабочие потоки).
  • Реализовать свойство привязки данных MVVM IsBusy намного проще.

Вам придется удалить класс BackgroundWorker и запустить новый вручную.нить (или используйте ThreadPool), если вы хотите переключиться на метод опроса.

2 голосов
/ 15 ноября 2010

Используйте DispatcherTimer для обновления свойства ElapsedTime в вашей модели представления. Это вызывается из потока GUI, так что вы можете напрямую привязать к свойству. Используйте BackgroundWorker для выполнения задачи потока.

public class ViewModel : INotifyPropertyChanged  
{
  public DateTime ElapsedTime {get; private set;}
  public bool IsRunning {get; private set;}

  private BackgroundWorker worker = new BackgroundWorker();
  private DateTime startTime;
  private DispatcherTimer t = new DispatcherTimer();

  public ViewModel()
  {
    t.Interval = 500; 
    t.Tick += (ox,ex) => UpdateTime();
    worker.DoWork += YourMethodHere;
    worker.RunWorkerCompleted += (ox,ex)=> {
      IsRunning = false;
      if (PropertyChanged != null)
       PropertyChanged(this, new PropertyChangedEventArgs("IsRunning"));
    };
  }

  public void UpdateTime()
  {
     ElapsedTime=startTime.Subtract(DateTime.Now);
     if (PropertyChanged != null)
       PropertyChanged(this, new PropertyChangedEventArgs("ElapsedTime"));
  }

  public void Start()
  {
    startTime=DateTime.Now;
    worker.RunWorkerAsync();
    IsRunning = true;
    if (PropertyChanged != null)
     PropertyChanged(this, new PropertyChangedEventArgs("IsRunning"));
  }

}

Вы можете привязать элемент графического интерфейса индикатора выполнения к свойству bool в модели представления, которое обновляется при запуске и завершении фоновой задачи (используйте событие RunWorkerComplete).

0 голосов
/ 15 ноября 2010

Я добился хороших результатов, когда мои рабочие потоки использовали методы обратного вызова для обновления коллекций в потоке пользовательского интерфейса, в которых хранятся данные о ходе выполнения и сообщениях, а также использовали отдельный таймер в потоке пользовательского интерфейса для обновления пользовательского интерфейса.Используйте блокировку вокруг коллекций, чтобы помнить параллелизм.Обратные вызовы также возвращают флаг продолжения / остановки рабочим потокам для плавного управления выключением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...