【问题标题】:Handle asynchronous events from model to view model处理从模型到视图模型的异步事件
【发布时间】:2015-07-28 17:05:47
【问题描述】:

假设一个模型异步触发事件(即不是从 UI 线程)。由于此模型可以访问 DAQ 卡并执行时间紧迫的任务,因此事件处理程序不要阻塞/花费很长时间来处理事件,这一点很重要。

总结一下:

  • 事件处理程序在处理事件时不应阻塞
  • 由于事件不是从 UI 线程触发的,因此它们需要委托给 UI 线程

现在我创建了一个使用视图的DispatcherBeginInvoke 的视图模型。在下面的代码中,我尝试更改 UI 可以在其上创建数据绑定的属性 IsEmergencyButtonActive

void board_EmergencyButtonEvent(object sender, EventArgs e, bool state)
{
    uiDispatcher.BeginInvoke((Action)(() => {
        IsEmergencyButtonActive = state;
        // Perform other time consuming tasks ...
    }));
}

这是“要走的路”吗?有没有更好的方法来解决这个任务?

【问题讨论】:

    标签: c# multithreading


    【解决方案1】:

    有没有更好的方法来解决这个任务?

    立即想到两个。

    首先是对您已有代码的小修改:您可以使用更通用的SynchronizationContext,而不是特定的调度程序:

    // elsewhere, from the UI thread...
    context = SynchronizationContext.Current;
    
    void board_EmergencyButtonEvent(object sender, EventArgs e, bool state)
    {
      context.Post(_ =>
      {
        IsEmergencyButtonActive = state;
        // Perform other time consuming tasks ...
      }, null);
    }
    

    在这个简化的代码中,它没有任何区别。但是,如果此代码位于 ViewModel 中,那么依赖 SynchronizationContext 比依赖特定于 UI 的调度程序类型更好。 (更容易在单元测试中使用,如果您更改为不同的 UI 框架,则更便携)。

    另一个选项——我更喜欢——是将您的事件流视为 Rx 流。因此,您将拥有IObservable<bool>,而不是EmergencyButtonEvent。然后消费代码如下所示:

    // IObservable<bool> EmergencyButtonStates { get; }
    IDisposable subscription = board.EmergencyButtonStates
        .ObserveOn(context) // Same "context" as above. Rx can also use dispatchers, too.
        .Subscribe(state =>
        {
          IsEmergencyButtonActive = state;
          // Perform other time consuming tasks ...
        });
    // Dispose "subscription" when you don't want any more events.
    

    Rx 有更多的学习曲线,但它非常强大。当涉及到任何与时间相关的逻辑时,它特别有用。例如,“将此事件流限制为每 200 毫秒不超过一次”或“当其他事件流在一秒钟内未更新时,从该事件流中获取最新值”,这可能对您基于硬件的设备很有用场景。

    【讨论】:

    • 您的解决方案听起来不错,尤其是 Rx 看起来很有希望!但是,我得出的结论是——从模型的 POV 来看——期望查看模型不会阻塞事件处理程序“太久”是一种不好的方法。模型本身应该异步触发事件,这样它就不必关心潜在的监听器做了什么。我想我在视图模型中保留了 SynchronizationContext 的方法但是我需要异步触发模型的事件本身。任何想法如何以简单的方式做到这一点?
    • @user1549233: 最简单的方法是使用 Rx - 这完全是关于异步事件流。另一种选择是让您的模型也采用 SyncCtx,但我认为 Rx 解决方案在谈论模型时具有更好的关注点分离故事。
    【解决方案2】:

    一般来说,我会推荐使用 async/await。事件处理程序是适合使用 async void 而不是 async Task 的少数场景之一。

    async void board_EmergencyButtonEvent(object sender, EventArgs e)
    {
        IsEmergencyButtonActive = taskBegunState;       
        await Task.Run(() => { ...Long Running Code... });
        IsEmergencyButtonActive = taskCompleteState;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-05
      • 1970-01-01
      • 1970-01-01
      • 2015-06-23
      • 1970-01-01
      相关资源
      最近更新 更多