BackgroundWorker с Dispatcher, похоже, ничего не делает - PullRequest
3 голосов
/ 17 ноября 2010

Я пытаюсь обновить ObservableCollection, то есть данные, связанные с пользовательским интерфейсом.Я знаю, что для этого мне нужно использовать Dispatcher и BeginvInvoke(), и чтобы сделать так, чтобы пользовательский интерфейс не зависал при этом, использование BackgroundWorker - хороший способ сделать это.В любом случае у меня все это есть, скомпилировано и запущено, но ничего не происходит.Мне нужно обновлять интерфейс каждые 2 минуты или около того, поэтому я также использую DispatcherTimer

. Это работает, потому что DispatcherTimer является частью Dispatcher, но замораживает интерфейс:

DispatcherTimer dispTimer = new DispatcherTimer();
dispTimer.Tick += dispTimer_Tick;
dispTimer.Interval = new TimeSpan(0, 0, 45);
dispTimer.Start();

private void dispTimer_Tick(object sender, EventArgs e)
{
    PartialEmployees.Clear();          
}

Итак, используя BackgroundWorker, я собрал следующее:

DispatcherTimer dispTimer = new DispatcherTimer();
dispTimer.Tick += dispTimer_Tick;
dispTimer.Interval = new TimeSpan(0, 0, 45);
dispTimer.Start();

private void dispTimer_Tick(object sender, EventArgs e)
{
    BackgroundWorker _worker = new BackgroundWorker();
    _worker.DoWork += DoWork;            
    _worker.RunWorkerAsync();

}

private void DoWork(object sender, DoWorkEventArgs e)
{            
    Dispatcher.CurrentDispatcher.BeginInvoke( new Action(()=> 
        {
            PartialEmployees.Clear();
        }));
} 

Но с пользовательским интерфейсом ничего не происходит.Что я пропускаю / не делаю правильно?

Ответы [ 3 ]

3 голосов
/ 17 ноября 2010

У вас есть две проблемы:

  1. Когда вы используете Dispatcher.CurrentDispatcher из фонового потока, он получает диспетчер фонового потока, а не диспетчер потока пользовательского интерфейса.

  2. Из вашего описания я понимаю, что ваш метод PartialEmployees.Clear() требует значительного времени для выполнения, и вы хотите избежать блокировки потока пользовательского интерфейса во время выполнения.Однако наличие BackgroundWorker для вызова PartialEmployees.Clear() в вашем потоке пользовательского интерфейса будет иметь тот же эффект, что и использование DispatcherTimer, поэтому вам нужно другое решение, чем то, которое вы ищете.

Есливам нужно только исправить проблему Dispatcher.CurrentDispatcher, просто сохраните текущий Dispatcher в локальной переменной следующим образом:

private void dispTimer_Tick(object sender, EventArgs e) 
{
  var uiDispatcher = Dispatcher.CurrentDispatcher;

  BackgroundWorker _worker = new BackgroundWorker(); 
  _worker.DoWork += (sender, e) =>
    uiDispatcher.BeginInvoke(new Action(() =>
    {
      PartialEmployees.Clear();
    }));
  _worker.RunWorkerAsync(); 
} 

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

  1. DispatcherTimer запускается в потоке пользовательского интерфейса.Все, что он делает (dispTimer_Tick), запускает BackgroundWorker, а затем завершает работу.
  2. BackgroundWorker выполняется в своем собственном therad.Все, что он делает, это планирует обратный вызов Dispatcher и затем завершается.
  3. Обратный вызов Dispatcher снова выполняется в потоке пользовательского интерфейса.Он вызывает PartialEmployees.Clear (), что занимает некоторое время, блокируя ваш поток пользовательского интерфейса во время его выполнения.

Таким образом, ваше поведение такое же, как если бы обратный вызов DispatcherTimer вызывал PartialEmployees.Clear () напрямую:В каждом случае трудоемкая операция выполняется в потоке пользовательского интерфейса.

Причина блокировки состоит в том, что каждый раз, когда вы выполняете большую часть работы над потоком пользовательского интерфейса, вы получаете мгновенную блокировку во время его выполнения.,Решение состоит в том, чтобы разбить вашу работу на более мелкие части и выполнять их по одному, либо из DispatcherTimer, либо из BackgroundWorker.В вашем случае проверьте код для PartialEmployees.Clear(), чтобы увидеть, можно ли это сделать постепенно.

1 голос
/ 17 ноября 2010

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

_worker.DoWork += delegate { DoWork(Dispatcher.CurrentDispatcher); };

...
private void DoWork(Dispatcher dispatcher) {
  dispatcher.BeginInvoke(new Action(() => {
    PartialEmployees.Clear();
  });
}
0 голосов
/ 19 ноября 2010

Я не думаю, что вам нужна фоновая работа, так как BeginInvoke на Dispatcher работает в потоке Threadpool.

что-то подобное должно работать и более кратко

DispatcherTimer dispTimer = new DispatcherTimer 
    {Interval = TimeSpan.FromSeconds(45)};
dispTimer.Tick += (o,e) => Dispatcher.CurrentDispatcher
    .BeginInvoke((Action)PartialEmployees.Clear);
dispTimer.Start();
...