C# Rx节流阀(…).ObserveOn(调度程序)和节流阀(…,调度程序)之间的差异

C# Rx节流阀(…).ObserveOn(调度程序)和节流阀(…,调度程序)之间的差异,c#,system.reactive,reactiveui,C#,System.reactive,Reactiveui,我有以下代码: IDisposable subscription = myObservable.Throttle(TimeSpan.FromMilliseconds(50), RxApp.MainThreadScheduler) .Subscribe(_ => UpdateUi()); 正如预期的那样,UpdateUi()将始终在主线程上执行。当我将代码更改为 IDisposable subscription

我有以下代码:

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)

在您给出的代码的两个示例中,
UpdateUi
将始终在
RxApp.MainThreadScheduler
指定的调度器上调用。我可以肯定地说,因为
ObserveOn
是一个decorator,它确保订阅者的
OnNext
处理程序在指定的调度程序上被调用。有关深入分析,请参阅

也就是说,这有点令人费解。
RxApp.MainThreadScheduler
未引用正确的调度程序调度程序,或者
UpdateUi
正在从调度程序线程过渡。前者并非史无前例——看看其他人在哪里遇到了这种情况。我不知道那件事是怎么回事。也许@PaulBetts可以参与进来,或者你可以在会议上提出一个问题。无论是什么情况,我都会仔细检查您的假设,因为我希望这是一个经过良好测试的领域。你有完整的复印件吗

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

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

如果
Throttle
指定了一个调度器,则在该调度器上执行节流-抑制的事件和释放的事件都将在该调度器上进行管理,订阅服务器也将在该调度器上调用

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

在大多数情况下,您最好在调度程序上限制ui事件,因为在后台线程上运行单独的计时器并支付上下文切换费用(如果只有一小部分事件将通过限制)通常成本更高


因此,为了检查您是否在
RxApp.MainThreadScheduler
中遇到问题,我将尝试通过另一种方法显式指定调度器或
SynchronizationContext
。如何做到这一点取决于您所在的平台-
ObserveOnDispatcher()
有望可用,或者使用合适的
ObserveOn
重载。如果导入了正确的
Rx
库,则可以选择控件、同步上下文和调度程序。

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

我不知道为什么,但似乎默认的
RxApp.MainThreadScheduler
无法正确初始化。默认实例是
waitforDispatchersScheduler
()。此类中的所有函数都依赖于
attemptoCreateScheduler

    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;
        }
    }
在我的例子中,似乎发生了
\u schedulerFactory()
抛出,导致返回
CurrentThreadScheduler.Instance


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

谢谢,我的
MainThreadScheduler
似乎有问题(请参阅)