带调度程序的BackgroundWorker似乎没有做任何事情

时间:2010-11-17 17:06:52

标签: c# mvvm backgroundworker dispatcher wpf-4.0

我正在尝试更新绑定到UI的数据ObservableCollection。我知道为了做到这一点,我需要使用DispatcherBeginvInvoke(),并且为了使UI在我这样做时不会冻结,使用BackgroundWorker是一个很好的方法去做它。无论如何,我拥有所有这些,编译和运行,但没有任何反应。我需要每2分钟左右更新一次用户界面,所以我也使用了DispatcherTimer

这很有效,因为DispatcherTimer是Dispatcher的一部分,但冻结了UI:

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();
        }));
} 

但UI没有任何反应。我错过了什么/没做错?

3 个答案:

答案 0 :(得分:3)

你有两个问题:

  1. 当您从后台线程使用Dispatcher.CurrentDispatcher时,它将获取后台线程的Dispatcher,而不是UI线程的Dispatcher。

  2. 根据您的描述,我收集您的PartialEmployees.Clear()方法需要花费大量时间来执行,并且您希望避免在执行期间锁定UI线程。但是,在UI线程上调用BackgroundWorker PartialEmployees.Clear()与使用DispatcherTimer具有相同的效果,因此您需要的解决方案与您的解决方案不同。

  3. 如果您只想解决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(); 
    } 
    

    这将导致您的UI更改生效,但在更改期间仍会锁定UI,就像您没有使用BackgroundWorker一样。原因是:

    1. DispatcherTimer在UI线程上执行。它所做的只是(dispTimer_Tick)启动一个BackgroundWorker,然后退出。
    2. BackgroundWorker在其自己的therad上执行。它只是安排一个Dispatcher回调然后退出。
    3. Dispatcher回调再次在UI线程上执行。它调用PartialEmployees.Clear()需要一段时间,在执行时锁定你的UI线程。
    4. 因此,您的行为与DispatcherTimer回调直接调用PartialEmployees.Clear()的行为相同:在每种情况下,都会在UI线程上执行耗时的操作。

      锁定的原因是,无论何时在UI线程上执行大量工作,您都会在运行时获得瞬间锁定。解决方案是将您的工作分成更小的部分,并从DispatcherTimer或BackgroundWorker中一次执行一个。在您的情况下,请检查PartialEmployees.Clear()的代码,看看是否可以逐步完成。

答案 1 :(得分:1)

这里的问题是您正在使用后台线程中的Dispatcher.CurrentDispatcher方法。您需要的是UI线程的Dispatcher实例。

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

...
private void DoWork(Dispatcher dispatcher) {
  dispatcher.BeginInvoke(new Action(() => {
    PartialEmployees.Clear();
  });
}

答案 2 :(得分:0)

我不认为您需要后台工作,因为BeginInvoke上的Dispatcher在Threadpool线程上运行。

这样的事情应该有效,而且更简洁

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