ObservableCollection上的BeginInvoke不是立即的

时间:2010-04-27 22:18:34

标签: c# wpf multithreading observablecollection begininvoke

在我的代码中,我订阅了一个在不同线程上发生的事件。每次发生此事件时,我都会收到一个发布到可观察集合的字符串:

    Dispatcher currentDispatcher = Dispatcher.CurrentDispatcher;
    var SerialLog = new ObservableCollection<string>();

    private void hitStation_RawCommandSent(object sender, StringEventArgs e)
    {
        string command = e.Value.Replace("\r\n", "");
        Action dispatchAction = () => SerialLog.Add(command);
        currentDispatcher.BeginInvoke(dispatchAction, DispatcherPriority.Render);
    }

下面的代码在我的视图模型中(可能在后面的代码中,在这种情况下无关紧要)。当我调用“hitstation.PrepareHit”时,上面的事件会被调用几次,然后我等待并调用“hitStation.HitBall”,上面的事件再被调用几次。

    private void HitBall()
    {
        try
        {
            try
            {
                Mouse.OverrideCursor = Cursors.Wait;

                //prepare hit
                hitStation.PrepareHit(hitSpeed);

                Thread.Wait(1000);
                PlayWarning();

                //hit
                hitStation.HitBall(hitSpeed);
            }
            catch (TimeoutException ex)
            {
                MessageBox.Show("Timeout hitting ball: " + ex.Message);
            }
        }
        finally
        {
            Mouse.OverrideCursor = null;
        }
    }

我遇到的问题是绑定到我的SerialLog的ListBox只有在HitBall方法完成时才会更新。我期待从PrepareHit看到一堆更新,暂停,然后从HitBall获得更多更新。

我尝试了几个DispatcherPriority参数,但它们似乎没有任何效果。

3 个答案:

答案 0 :(得分:4)

我认为你是在封锁自己。

UI Thread在Thread.Wait上等待,BeginInvoke将动作发送到dipatcher,但是UI Thread正忙着等待。这就是为什么UI更新只在HitBall完成之后完成,也就是说,当UI线程完成处理时。

要解决这个问题,你应该在另一个线程中启动HitBall方法的代码,释放UI:

 private void HitBall() 
 { 
     try {

         Mouse.OverrideCursor = Cursors.Wait;
         Dispatcher dispatcher = Dispatcher.CurrentDispatcher;

         Action hitOperations = () =>
         {
             hitStation.PrepareHit(hitSpeed);

             Thread.Wait(1000);

             //Only needed if PlayWarning needs access to UI Thread
             dispatcher.Invoke(() => PlayWarning());

             hitStation.HitBall(hitSpeed);

             dispatcher.Invoke(() => Mouse.OverrideCursor = null);
          };

          //Free the UI from work, start operations in a background thread
          hitOperations.BeginInvoke(null, null);

      }
      catch (TimeoutException ex)
      {
          MessageBox.Show("Timeout hitting ball: " + ex.Message);
      }
 }

此外,如果Thread.Wait(1000)的预期用途是等待事件刷新UI,则不再需要此实现。

答案 1 :(得分:1)

是否需要在ViewModel上引发PropertyChanged事件这么简单?

答案 2 :(得分:0)

除非有上下文切换,否则您可能看不到来自PrepareHit的更新,并且无法保证何时会发生上下文切换(除非您阻止当前线程并且这会增加上下文切换发生的可能性) )。

正如iCe所提到的,如果你在UI线程上这样做,那么你可能会阻止你的UI。当您致电Thread.Wait时,您的用户界面会阻止/冻结吗?如果没有,则继续阅读以下内容:

<强>更新
我想不出任何不会影响并发性的事情......如果不妥协并发性,你可以尝试提高BeginInvoke的优先级:http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherpriority.aspx

currentDispatcher.BeginInvoke(dispatchAction, DispatcherPriority.Send);

顺便说一句,看起来Render优先级低于Normal优先级:

  

渲染枚举值为7。   同时处理的操作   优先级为渲染。

     

DataBind   枚举值为8.操作是   以与数据相同的优先级处理   捆绑。

     

正常枚举值   是9.操作在。处理   正常优先。这是典型的   申请优先权。

     

发送   枚举值为10.操作   在其他之前处理   异步操作。这是   最高优先级。