none
ObserveOn doesn't observe on requested thread?

    Question

  • private void TestObserveOn()
    {
     Console.WriteLine("Root Thread={0}",Thread.CurrentThread.ManagedThreadId);
    
     var s1 = Observable.Interval(TimeSpan.FromSeconds(1),Scheduler.NewThread);
     var s2 = Observable.Interval(TimeSpan.FromSeconds(1),Scheduler.NewThread);
    
     var s0 = s1.Merge(s2).ObserveOn(Scheduler.CurrentThread);
    
     s0.Subscribe(i => Console.WriteLine("i={0} Thread={1}", i, Thread.CurrentThread.ManagedThreadId));
    
     Console.WriteLine("press any key...");
     Console.ReadLine();
    }
    

    Output

    Root Thread=1
    press any key.
    i=0 Thread=3
    i=0 Thread=4
    i=1 Thread=3
    i=1 Thread=4
    i=2 Thread=3
    i=2 Thread=4
    i=3 Thread=3
    i=3 Thread=4
    
    I expect all events to occur on a root thread (1 in the example below). What am I doing wrong?

    Thanks.

    Thursday, July 07, 2011 7:12 AM

Answers

  • Hi,

    The CurrentThread scheduler doesn't mean the thread on which the query was created, it means the current thread on which the query is executing.  In this respect, it's similar to the Immediate scheduler.  The difference is that the CurrentThread scheduler uses a single queue for all scheduled actions to support single-threaded cooperative multitasking, whereas the Immediate scheduler executes a scheduled action immediately, regardless of whether a previously scheduled action is still executing.

    In your particular example, ObserveOn(Scheduler.CurrentThread) will have the same exact behavior as not specifying ObserveOn at all.

    If you want to ensure that observations are marshalled onto the UI thread, then for WPF, Silverlight and WP7 use ObserveOnDispatcher and for WinForms use ObserveOn(Control control).  Both of these extensions require an additional assembly reference to either System.Reactive.Windows.Threading.dll or System.Reactive.Windows.Forms.dll, respectively.

    For ASP.NET and other UI platforms use ObserveOn(SynchronizationContext).  Just make sure that the platform supports SynchronizationContext.

    - Dave


    http://davesexton.com/blog
    • Marked as answer by tivadj2 Friday, July 08, 2011 7:01 AM
    Thursday, July 07, 2011 1:37 PM

All replies

  • Hi,

    The CurrentThread scheduler doesn't mean the thread on which the query was created, it means the current thread on which the query is executing.  In this respect, it's similar to the Immediate scheduler.  The difference is that the CurrentThread scheduler uses a single queue for all scheduled actions to support single-threaded cooperative multitasking, whereas the Immediate scheduler executes a scheduled action immediately, regardless of whether a previously scheduled action is still executing.

    In your particular example, ObserveOn(Scheduler.CurrentThread) will have the same exact behavior as not specifying ObserveOn at all.

    If you want to ensure that observations are marshalled onto the UI thread, then for WPF, Silverlight and WP7 use ObserveOnDispatcher and for WinForms use ObserveOn(Control control).  Both of these extensions require an additional assembly reference to either System.Reactive.Windows.Threading.dll or System.Reactive.Windows.Forms.dll, respectively.

    For ASP.NET and other UI platforms use ObserveOn(SynchronizationContext).  Just make sure that the platform supports SynchronizationContext.

    - Dave


    http://davesexton.com/blog
    • Marked as answer by tivadj2 Friday, July 08, 2011 7:01 AM
    Thursday, July 07, 2011 1:37 PM
  • Dave's answer is spot on, but there is a way to more or less get the semantics that you wanted.

    Try changing the `Merge` line to these:

    var els = new System.Reactive.Concurrency.EventLoopScheduler();
    var s0 = s1.Merge(s2).ObserveOn(els);
    

    Cheers.


    James C-S
    Thursday, July 07, 2011 2:16 PM
  • Thanks Dave and James.

    I saw

    .ObserveOn(SynchronizationContext.Current)
    .ObserveOnDispatcher();
    
    both work under WPF. Is there preference to use one of them?
    Friday, July 08, 2011 7:03 AM
  • Hi,

    I believe the differences are as follows:

    1. The primary difference between SynchronizationContext and Dispatcher is that the former is a cross-platform generalization whereas the latter is specifically designed for WPF.
    2. The Dispatcher is available via a property on all controls in WPF (actually, anything that derives from DependencyObject), so even from different threads you can easily retrieve a reference to the UI's Dispatcher.
    3. The SynchronizationContext implementation in WPF is actually DispatcherSynchronizationContext, which simply wraps and delegates all calls to a Dispatcher.
    4. The current Dispatcher in WPF automatically sets SynchronizationContext.Current to a new DispatcherSynchronizationContext that wraps it.
    5. The ObserveOnDispatcher method creates a new DispatcherSynchronizationContext object for the current Dispatcher and passes it to the ObserveOn(SynchronizationContext) method.

    So in WPF it certainly makes sense to use the Dispatcher directly whenever synchronization is needed.

    In Rx, it makes sense to use ObserveOnDispatcher, although the cost is that a new DispatcherSynchronizationContext will be created for the current Dispatcher, instead of the existing one being used.  Essentially, it's the same thing as calling ObserveOn(SynchronizationContext.Current) as long as the Current property is definitely assigned to the current Dispatcher already, which it most likely will be in WPF if you're calling it from the UI thread, and it won't be from any other thread.

    - Dave


    http://davesexton.com/blog

    • Edited by Dave Sexton Friday, July 08, 2011 2:54 PM Added more specifcs to last sentence.
    Friday, July 08, 2011 2:49 PM
  • Hi,

    Note that if you're creating a query on a thread other than the Dispatcher thread, then neither will work.  In that case you can use the ObserveOn(Dispatcher) overload as an alternative.  Simply pass in the Dispatcher object that is returned by any control's Dispatcher property.

    - Dave


    http://davesexton.com/blog
    Friday, July 08, 2011 2:58 PM
  • Dave, thanks for explanation.
    Friday, July 08, 2011 7:40 PM