I have an observable which represents a stream of stock prices. If there are no observers on my observable sequence I'd like to be able to disconnect from the remote server that is supplying the stream of prices, but I don't want to do that until every observer has called Dispose(). Then in a similar fashion, when the first person calls Subscribe I'd like to reconnect to the remote server.
Is there a way to figure out how many observers have called subscribe on an observable? Or perhaps a way to know when observers are calling Subscribe or Dispose?
I would simply use RefCount / Publish. I always feel like if I'm implementing IObservable I'm working way too hard.
myColdObservable.Publish().RefCount();
This will make your observable stop pulsing after everyone has disconnected. Here's a sample:
var coldObservable = Observable
.Interval(TimeSpan.FromSeconds(1))
.ObserveOn(Scheduler.TaskPool)
.Select(_ => DoSomething());
var refCountObs = coldObservable.Publish().RefCount();
CompositeDisposable d = new CompositeDisposable();
d.Add(refCountObs.Subscribe(n => Console.WriteLine("First got: " + n)));
d.Add(refCountObs.Subscribe(n => Console.WriteLine("Second got: " + n)));
d.Add(refCountObs.Subscribe(n => Console.WriteLine("Third got: " + n)));
//Wait a bit for work to happen
System.Threading.Thread.Sleep(10000);
//Everyone unsubscribes
d.Dispose();
//Observe that DoSomething is not called.
System.Threading.Thread.Sleep(3000);
This does not cover the case where you actually want to know the number of subscribers, but I think this fits with your requirements of stopping work if there are no subscribers.
Bit of an old one but I came across this post as I had a problem where I needed to know the number of subscribers. Using Bart's suggestion I came up with this extension.
public static IObservable<T> CountSubscribers<T>(this IObservable<T> source, Action<int> countChanged)
{
int count = 0;
return Observable.Defer(() =>
{
count = Interlocked.Increment(ref count);
countChanged(count);
return source.Finally(() =>
{
count = Interlocked.Decrement(ref count);
countChanged(count);
});
});
}
In general, don't implement IObservable; typically there's already soemthing in Rx that can help you out, either directly or through composition. If you ever have to implement IObservable, use Observable.Create to do so, in order to get all the guaranteed required for the observer contract etc.
As for your problem - the suggestion of using Publish and RefCount is exactly the composition you're looking for. If you want to count yourself for some reason, use Observable.Defer to intercept subscriptions, possibly with Observable.Finally to intercept sequence terminations. Or, wrap the source with an Observable.Create, forward the observer to the wrapped sequence, and wrap the returned IDisposable with counting logic (using Disposable.Create).
Cheers,
-Bart (Rx team)
IObservable<T> is an interface that you can implement. In the Subscribe method of the interface you can keep track of observers by maintaining a list internally.
Following code snippet is from MSDN.
private List<IObserver<Location>> observers;
public IDisposable Subscribe(IObserver<Location> observer)
{
if (! observers.Contains(observer))
observers.Add(observer);
// ------- If observers.Count == 1 create connection. -------
return new Unsubscriber(observers, observer);
}
private class Unsubscriber : IDisposable
{
private List<IObserver<Location>>_observers;
private IObserver<Location> _observer;
public Unsubscriber(List<IObserver<Location>> observers, IObserver<Location> observer)
{
this._observers = observers;
this._observer = observer;
}
public void Dispose()
{
if (_observer != null && _observers.Contains(_observer))
_observers.Remove(_observer);
// ----------- if observers.Count == 0 close connection -----------
}
}
Related
I'm trying to notify listeners who subscribed to Subject _sub from another observable and after that log some message in Do handler. I'm calling OnNext and everything would work fine if _sub wasn't asynchronous. The problem here is that there is no OnNextAsync function which I would await in the first observable. What is the best way to do this?
class Program
{
private static Subject<int> _sub = new Subject<int>();
static void Main(string[] args)
{
_sub.SelectMany(async _ =>
{
Console.WriteLine("SUB START: " + _);
await Task.Delay(3000);
Console.WriteLine("SUB END: " + _);
return 1;
}).Subscribe();
Start();
}
public static void Start()
{
int count = 0;
Observable.Interval(TimeSpan.FromSeconds(5)).Select(x =>
{
Console.WriteLine("START INTERVAL");
_sub.OnNext(count++); //onNext is not awaitable
Console.WriteLine("END INTERVAL");
return 1;
})
.Do(_ => Console.WriteLine("ALL FINISHED"))
.Subscribe();
Console.WriteLine("READLINE");
Console.ReadLine();
}
}
Result:
READLINE
START INTERVAL
SUB START: 0
END INTERVAL
ALL FINISHED
SUB END: 0
Expected result:
READLINE
START INTERVAL
SUB START: 0
SUB END: 0
END INTERVAL
ALL FINISHED
An observable should not rely on the behavior of its observers.
I suggest you rethink the whole thing. What you're doing looks more interactive than reactive.
I'm going to echo Paulo for clarity:
Observables shouldn't care about Observers. While an Observable will wait on its synchronous Observers, it's best to think of this as an accident of implementation. An observable doesn't wait at all on an asynchronous Observer. Either way, the waiting shouldn't be relied upon.
You should really re-think how you're doing this. You're using a reactive library to write interactive code. Probably either the wrong choice of tool or a misuse of the tool.
Your code is littered with Rx code-smells. Think of reactive code as a flowchart. A flowchart of your code would look like spaghetti. It should look more like a binary tree.
This sounds like an XY problem: I would suggest re-phrasing your question with what you're trying to accomplish.
I'm trying to write a windows service whose producers and consumers work like this:
Producer: At scheduled times, get all unprocessed items (Processed = 0 on their row in the db) and add each one to the work queue that isn't already in the work queue
Consumer: Constantly pull items from the work queue and process them and update the db (Processed = 1 on their row)
I've tried to look for examples of this exact data flow in C#.NET so I can leverage the existing libraries. But so far I haven't found exactly that.
I see on https://blog.stephencleary.com/2012/11/async-producerconsumer-queue-using.html the example
private static void Produce(BufferBlock<int> queue, IEnumerable<int> values)
{
foreach (var value in values)
{
queue.Post(value);
}
queue.Complete();
}
private static async Task<IEnumerable<int>> Consume(BufferBlock<int> queue)
{
var ret = new List<int>();
while (await queue.OutputAvailableAsync())
{
ret.Add(await queue.ReceiveAsync());
}
return ret;
}
Here's the "idea" of what I'm trying to modify that to do:
while(true)
{
if(await WorkQueue.OutputAvailableAsync())
{
ProcessItem(await WorkQueue.ReceiveAsync());
}
else
{
await Task.Delay(5000);
}
}
...would be how the Consumer works, and
MyTimer.Elapsed += Produce;
static async void Produce(object source, ElapsedEventArgs e)
{
IEnumerable<Item> items = GetUnprocessedItemsFromDb();
foreach(var item in items)
if(!WorkQueue.Contains(w => w.Id == item.Id))
WorkQueue.Enqueue(item);
}
...would be how the Producer works.
That's a rough idea of what I'm trying to do. Can any of you show me the right way to do it, or link me to the proper documentation for solving this type of problem?
Creating a custom BufferBlock<T> that rejects duplicate messages is anything but trivial. The TPL Dataflow components do not expose their internal state for the purpose of customization. You can see here an attempt to circumvent this limitation, by creating a custom ActionBlock<T> with an exposed IEnumerable<T> InputQueue property. The code is lengthy and obscure, and creating a custom BufferUniqueBlock<T> might need double the amount of code, because this class implements the ISourceBlock<T> interface too.
My suggestion is to find some other way to avoid processing twice an Item, instead of preventing duplicates from entering the queue. Maybe you could add the responsibility to the Consumer to query the database, and check if the currently received item is unprocessed, before actually processing it.
Is there a subclass of the Observable<T> class that exposes access to Notify<T>(T value) method (or access Notifier, extension method) so that we can call observable.Notify(t) at hoc resulting all subscriptions being notified (OnNext invoked on the same thread).
I am not interested in FromEventPattern.
The ISubject<T> interface inherits both IObservable<T> and IObserver<T>.
var subject = new Subject<string>();
subject.Subscribe(text => Console.WriteLine(text));
subject.OnNext("Hello");
subject.OnNext("World!");
IObserver<T>.OnNext is essentially your Notify method.
More reading:
Using Subjects (MSDN)
Subject<T> (Introduction to Rx)
You certainly can use Subject<string> to provide an object that is an observer (i.e. you can call .OnNext(string)) and is an observable that can be subscribed to.
The only downside is that any code with access to your Subject<string> can also call .OnCompleted() or .OnError(...) and break your subscribers' code.
The other alternative is to do this:
Action<string> notify = null;
var observable = Observable.FromEvent<string>(h => notify += h, h => notify -= h);
var subscription = observable.Subscribe(x => Console.WriteLine(x));
notify("Hello");
You now have a simple Action<string> delegate that you can call to push a value and just so long as you keep the subscription alive clients can't kill your program with rogue calls to .OnCompleted() or .OnError(...).
Taking my first steps with Rx I am stuck here:
public class DisposableResourceDemo : IDisposable
{
public DisposableResourceDemo() {
Console.WriteLine("DisposableResourceDemo constructor.");
}
public void Dispose() {
Console.WriteLine("DisposableResourceDemo.Dispose()");
}
public void SideEffect() {
Console.WriteLine("DisposableResourceDemo.SideEffect()");
}
}
[Test]
public void ShowBehaviourOfRxUsing()
{
var test = Observable.Using(() =>
{
// This should happen exactly once, independent of number of subscriptions,
// object should be disposed on last subscription disposal or OnCompleted call
return new DisposableResourceDemo();
},
(dr) =>
{
return Observable.Create<string>(
(IObserver<string> observer) =>
{
dr.SideEffect();
var dummySource = Observable.Return<string>("Some Text");
return dummySource.Subscribe(observer);
});
}).Publish().RefCount();
Console.WriteLine("before 1st subscription.");
test.Subscribe(Console.WriteLine, () => Console.WriteLine("OnCompleted in 1st."));
Console.WriteLine("before 2nd subscription.");
test.Subscribe(Console.WriteLine, () => Console.WriteLine("OnCompleted in 2nd."));
}
To my surprise the code above yields
before 1st subscription.
DisposableResourceDemo constructor.
DisposableResourceDemo.SideEffect()
Some Text
OnCompleted in 1st.
DisposableResourceDemo.Dispose()
before 2nd subscription.
--> [happy with missing "Some Text" here]
OnCompleted in 2nd.
--> [unhappy with second instantiation here]
DisposableResourceDemo constructor.
DisposableResourceDemo.SideEffect()
DisposableResourceDemo.Dispose()
Please note that calling Connect() manually after both subscriptions is not what I want here, though then the output is as expected.
I am not totally sure what you are trying to achieve here. It seems that you want to share the observable sequence and its related resources. So the standard ways to do this is with the ConnectableObservable types that you get from .Replay() and .Publish() etc
You say you dont want to use .Connect() and instead you use .RefCount() which is very common. However, your sequence completes. You also are using the Extension method Subscribe(...) which will internally create an Auto detaching observer, i.e. when the sequence completes, it will disconnect.
So my question is, should the internal sequence actually complete?
If the answer is yes, then why would the 2nd subscription get the OnComplete notification...it has happened already, it is in the past. Maybe you do want to replay the OnComplete, in which case maybe .Replay(1) is what you want.
If the answer is no, then you can easily fix this by putting a Concat(Observable.Never<string>()) either before the .Publish() or after the Observable.Return.
I have a service that I would like to turn into a rx observable
The service has an interface of
IEnumerable<Price> FetchUpdatedPrices()
{
//do work to return changed data since last update
}
My idea was to use rx to allow a consumer to subscribe to updates. The implementation would poll the service every x seconds and call the observer.
I came up with the following
public IDisposable Subscribe(IObserver<IEnumerable<Price>> observer)
{
IObservable<IEnumerable<Price>> updatedPrices = Observable.Interval(new TimeSpan(0, 0, 1))
.Select(r => FetchUpdatedPrices());
return updatedPrices.Subscribe(observer);
}
The problem is I would like the observer to see an IObservable<Price> rather than an IObservable<IEnumerable<Price>>
Could anyone give this Rx noob any pointers on how to do this?
How about SelectMany?
IObservable<IEnumerable<Price>> updatedPrices = Observable.Interval(new TimeSpan(0, 0, 1))
.SelectMany(r => FetchUpdatedPrices());