I have the following code:
IDisposable subscription = myObservable.Throttle(TimeSpan.FromMilliseconds(50), RxApp.MainThreadScheduler)
.Subscribe(_ => UpdateUi());
As expected, UpdateUi()
will always execute on the main thread. When I change the code to
IDisposable subscription = myObservable.Throttle(TimeSpan.FromMilliseconds(50))
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ => UpdateUi());
UpdateUI()
will be executed in a background thread.
Why is not Throttle(...).ObserveOn(scheduler)
equivalent to Throttle(..., scheduler)
?
Rx introduces a very handy mechanism for introducing concurrency and multithreading to your code: Scheduling. In the Rx world, there are generally two things you want to control the concurrency model for: As you could probably guess, these are exposed via two extension methods to IObservable<T> called SubscribeOn and ObserveOn.
B.inner.Leaf. ThreadId:10 It is worth noting that there is also a TestScheduler accompanied by its base classes VirtualTimeScheduler and VirtualTimeSchedulerBase . The latter two are not really in the scope of an introduction to Rx, but the former is.
Like events, Rx is just a way of chaining callbacks together for a given notification. While Rx is a free-threaded model, this does not mean that subscribing or calling OnNext will introduce multi-threading to your sequence. Being free-threaded means that you are not restricted to which thread you choose to do your work.
Many implementations of ReactiveX use “ Scheduler s” to govern an Observable’s transitions between threads in a multi-threaded environment. You can instruct an Observable to send its notifications to observers on a particular Scheduler by means of the ObserveOn operator.
In both examples in code you've given UpdateUi
will always be invoked on the scheduler specified by RxApp.MainThreadScheduler
. I can say this with some certainty since ObserveOn
is a decorator that ensures the OnNext
handler of subscribers is called on the specified scheduler. See here for an in-depth analysis.
So that said, this is a bit puzzling. Either RxApp.MainThreadScheduler
is not referring to the correct dispatcher scheduler or UpdateUi
is transitioning off the dispatcher thread. The former is not unprecedented - see https://github.com/reactiveui/ReactiveUI/issues/768 where others have run into this. I have no idea what the issue was in that case. Perhaps @PaulBetts can weigh in, or you could raise an issue at https://github.com/reactiveui/. Whatever the case, I would carefully check your assumptions here since I would expect this to be a well tested area. Do you have a complete repro?
As to your specific question, the difference between Throttle(...).ObserveOn(scheduler)
and Throttle(..., scheduler)
is as follows:
In the first case when Throttle
is specified without a scheduler it will use the default platform scheduler to introduce the concurrency necessary to run it's timer - on WPF this would use a thread pool thread. So all the throttling will be done on a background thread and, due to the following ObserveOn
the released events only will be passed to the subscriber on the specified scheduler.
In the case where Throttle
specifies a scheduler, the throttling is done on that scheduler - both suppressed events and released events will be managed on that scheduler and the subscriber will be called on that same scheduler too.
So either way, the UpdateUi
will be called on the RxApp.MainThreadScheduler
.
You are best off throttling ui events on the dispatcher in most cases since it's generally more costly to run separate timers on a background thread and pay for the context switch if only a fraction of events are going to make it through the throttle.
So, just to check you haven't run into an issue with RxApp.MainThreadScheduler
, I would try specifying the scheduler or SynchronizationContext
explicitly via another means. How to do this will depend on the platform you are on - ObserveOnDispatcher()
is hopefully available, or use a suitable ObserveOn
overload. There are options for controls, syncronizationcontexts and schedulers given the correct Rx
libraries are imported.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With