我有以下代码:
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)
?
答案 0 :(得分:5)
经过一番调查后,我认为这是由于运行时使用的Rx版本比我预期的不同(我为第三方应用程序开发了一个插件)。
我不确定原因,但似乎默认RxApp.MainThreadScheduler
无法正确初始化。默认实例是WaitForDispatcherScheduler
(source)。此类中的所有函数都依赖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.MainThreadScheduler
到new 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
库的情况下,可以选择控件,同步加密和调度程序。