Rx Throttle(...)。ObserveOn(调度程序)和Throttle(...,调度程序)之间的区别

时间:2015-03-02 17:37:29

标签: c# system.reactive reactiveui

我有以下代码:

IDisposable subscription = myObservable.Throttle(TimeSpan.FromMilliseconds(50), RxApp.MainThreadScheduler)
                                       .Subscribe(_ => UpdateUi());

正如预期的那样,UpdateUi()将始终在主线程上执行。当我将代码更改为

IDisposable subscription = myObservable.Throttle(TimeSpan.FromMilliseconds(50))
                                       .ObserveOn(RxApp.MainThreadScheduler)
                                       .Subscribe(_ => UpdateUi());

UpdateUI()将在后台线程中执行。

为什么Throttle(...).ObserveOn(scheduler)不等同于Throttle(..., scheduler)

2 个答案:

答案 0 :(得分:5)

经过一番调查后,我认为这是由于运行时使用的Rx版本比我预期的不同(我为第三方应用程序开发了一个插件)。

我不确定原因,但似乎默认RxApp.MainThreadScheduler无法正确初始化。默认实例是WaitForDispatcherSchedulersource)。此类中的所有函数都依赖attemptToCreateScheduler

    IScheduler attemptToCreateScheduler()
    {
        if (_innerScheduler != null) return _innerScheduler;
        try {
            _innerScheduler = _schedulerFactory();
            return _innerScheduler;
        } catch (Exception) {
            // NB: Dispatcher's not ready yet. Keep using CurrentThread
            return CurrentThreadScheduler.Instance;
        }
    }

在我的案例中似乎发生了_schedulerFactory()抛出,导致CurrentThreadScheduler.Instance被返回。

通过手动初始化RxApp.MainThreadSchedulernew SynchronizationContextScheduler(SynchronizationContext.Current)行为符合预期。

答案 1 :(得分:3)

在代码中的两个示例中,您UpdateUi 总是会在RxApp.MainThreadScheduler指定的调度程序上调用。我可以肯定地说这一点,因为ObserveOn是一个装饰器,可以确保在指定的调度程序上调用订阅者的OnNext处理程序。请参阅here进行深入分析。

所以说,这有点令人费解。 RxApp.MainThreadScheduler未引用正确的调度程序调度程序或UpdateUi正在转移调度程序线程。前者不是前所未有的 - 见https://github.com/reactiveui/ReactiveUI/issues/768其他人遇到过这种情况。我不知道那个案子的问题是什么。也许@PaulBetts可以权衡,或者您可以在https://github.com/reactiveui/提出问题。无论如何,我会仔细检查你的假设,因为我希望这是一个经过良好测试的领域。你有一个完整的复制品吗?

关于您的具体问题,Throttle(...).ObserveOn(scheduler)Throttle(..., scheduler)之间的区别如下:

在第一种情况下,如果没有调度程序指定Throttle,它将使用默认平台调度程序来引入运行它所需的并发计时器 - 在WPF上,这将使用线程池线程。因此,所有限制都将在后台线程上完成,并且由于以下ObserveOn,已释放的事件将仅传递给指定调度程序上的订阅者。

Throttle指定调度程序的情况下,在该调度程序上完成限制 - 将在该调度程序上管理已抑制事件和已释放事件,并且也将在同一调度程序上调用订阅者。

无论哪种方式,都会在UpdateUi上调用RxApp.MainThreadScheduler

在大多数情况下,最好在调度程序上限制ui事件,因为在后台线程上运行单独的计时器通常成本更高,如果只有一小部分事件要进行上下文切换,则需要付费通过油门。

因此,为了检查您是否遇到RxApp.MainThreadScheduler的问题,我会尝试通过其他方式明确指定调度程序或SynchronizationContext。如何执行此操作取决于您所在的平台 - 希望ObserveOnDispatcher()可用,或使用合适的ObserveOn重载。在导入正确的Rx库的情况下,可以选择控件,同步加密和调度程序。