Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cross thread exception when using RX Throttle

I am getting

Invalid cross-thread access.

When using RX Throttle

Here is my code:

        yObs.SubscribeOnDispatcher()
            .DistinctUntilChanged()
            .Throttle(TimeSpan.FromMilliseconds(33))
            .SkipWhile(y => !_isDragging)
            .Subscribe(y =>
                           {
                               // Exception when trying to access image
                               image.RenderTransform = new CompositeTransform() { TranslateY = -y };
                               _vm.UpdateContentDrag(y / image.ActualHeight * 100);
                           });

But if I omit throttle everything works.

As far as I understand Throttle uses thread pool so OnNext doesn't happen on UI thread. But SubscribeOnDispatcher should marshal it back to the UI thread. Shouldn't it?

like image 642
Vitalij Avatar asked Jan 07 '12 02:01

Vitalij


2 Answers

Your understanding of SubscribeOnDispatcher is incorrect. First of all, let's distinguish between two *On operators:

  • SubscribeOn* - Runs the (un)subscription logic on the specified scheduler. Rarely used, unless you play with Observable.Create etc.
  • ObserveOn* - Runs the observer messages (OnNext, OnError, OnCompleted) on the specified scheduler. Mostly used for UI synchronization when running the "event handlers" passed to Subscribe.

In order for your sample to work, you also should stick the ObserveOn operator use further downstream in the query. Our recommendation is to do so right in front of the final Subscribe call. Within the query, concurrency can be introduced by operators such as Throttle (whose default scheduler is the thread pool). Only at the point you need synchronization guarantees, introduce a *On operator.

Paul's suggestion to parameterize the Throttle call is a good one too. In cases where you can control all the concurrency introduced, you may want to do so. However, there are many cases where you are handed an IObservable sequence that is ill-behaved with regards to synchronization requirements, requiring the use of *On operators.

like image 175
Bart De Smet Avatar answered Nov 10 '22 12:11

Bart De Smet


Just change the line to:

.Throttle(TimeSpan.FromMilliseconds(33), DispatcherScheduler.Instance)

It's more efficient anyways (though 33ms is a really short timespan throttle, hitting up against the timer resolution)

like image 6
Ana Betts Avatar answered Nov 10 '22 11:11

Ana Betts