简体   繁体   中英

How to implement Switch for IObservable<IObserver<T>> for reactive extensions in C#

In reactive extensions we have

IObservable<T> Switch(this IObservable<IObservable<T>> This)

I would like an implementation of

IObserver<T> Switch(this IObservable<IObserver<T>> This)

which would switch the outgoing events to different observers but presented as a single observer.

This version handles a couple of issues:

  • There's a race condition that can result in lost events. If the observer observes an event on one thread while the source observable produces a new observer on another thread, if you do not use any sort of synchronization, you could end up calling OnCompleted on the current observer on one thread just before the other thread calls OnNext on that same observer. This will result in the event being lost.

  • Related to the above, by default, observers are not thread-safe. You should never concurrent calls to an observer or you will violate a primary Rx contract. Without any locking, the subscriber might call OnCompleted on the currentObserver while another thread is calling OnNext on that same observer. Out of the box, this sort of thing can be solved by using a Synchronized Subject. But since we need synchronization also for the previous problem, we can just use a simple mutex.

  • We need a way to unsubscribe from the source observable. I'm supposing that when the resulting observer is completed (or errored), this is a good time to unsubscribe from source since our observer has been told to expect no more events.

Here's the code:

public static IObserver<T> Switch<T>(this IObservable<IObserver<T>> source)
{
    var mutex = new object();
    var current = Observer.Create<T>(x => {});
    var subscription = source.Subscribe(o =>
    {
        lock (mutex)
        {
           current.OnCompleted();
           current = o;
        }
    });

    return Observer.Create<T>(
        onNext: v =>
        {
            lock(mutex)
            {                
              current.OnNext(v);
            }
        },
        onCompleted: () =>
        {
             subscription.Dispose();
             lock (mutex)
             {
                 current.OnCompleted();
             }
        },
        onError: e =>
        {
             subscription.Dispose();
             lock (mutex)
             {
                 current.OnError(e);
             }
        });
}
public static IObserver<T> Switch<T>(this IObservable<IObserver<T>> This)
{
    IObserver<T> currentObserver = Observer.Create<T>(x => { });

    This.Subscribe(o => { currentObserver.OnCompleted(); currentObserver = o; });


    return Observer.Create<T>
        ( onNext: v => currentObserver.OnNext(v)
        , onCompleted: () => currentObserver.OnCompleted()
        , onError: v => currentObserver.OnError(v));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM