Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The difference between Rx Throttle(...).ObserveOn(scheduler) and Throttle(..., scheduler)

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)?

like image 531
larsmoa Avatar asked Mar 02 '15 17:03

larsmoa


People also ask

How does RX scheduling work?

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.

Is there a testscheduler in RX?

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.

What is the difference between events and RX?

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.

What is a scheduler In ReactiveX?

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.


1 Answers

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.

like image 157
James World Avatar answered Oct 04 '22 05:10

James World