I needed to alternate between two states with each state having a different interval time.
The best way I could think of doing this was to use Reactive Extensions' Observable.Generate
which is pretty awsome.
From what I read on msdn and other sites, Observable.Finally() should fire if the
observable "terminates gracefully or exceptionally". I was testing the following code
(in LINQPad) to see how it works, but I can not get .Finall() to fire at all.
var ia = TimeSpan.FromSeconds(1);
var ib = TimeSpan.FromSeconds(.2);
var start = DateTime.UtcNow;
var ct = new CancellationTokenSource();
var o = Observable.Generate(
true,
// s => !ct.IsCancellationRequested,
s => (DateTime.UtcNow-start) < TimeSpan.FromSeconds(3) && !ct.IsCancellationRequested,
s => !s,
s => s ? "on" : "off",
s => s? ib : ia)
// .TakeUntil(start+TimeSpan.FromSeconds(3))
.Concat(Observable.Return("end"));
o.Subscribe( s=> s.Dump(), ct.Token);
var t = o.ToTask(ct.Token);
t.ContinueWith(x => x.Dump("done"));
o.Finally(() => "finallY".Dump()); // never gets called?
Thread.Sleep(10000);
ct.Cancel();
If I make Thread.Sleep 10s, the observable sequence finishes and the Task.ContinueWith fires,
but not .Finally().
If I make Thread.Sleep 2s, the observable sequence is canceled and the Task.ContinueWith again fires,
but not .Finally().
Why not?
Look at the return type of the Finally method; should give you a hint. Just like the Concat method returns a new IObservable with the new sequence concatenated to it, but doesn't change the original, the Finally method returns a new IObservable that has that final action, but you're subscribing to the original IObservable. Put the following line in front of your Subscribe call and it'll work.
o = o.Finally(() => "finallY".Dump());
I agree it's an odd API choice though; I'd think of Finally as being more akin to Subscribe than to Concat. You're subscribing to the finally "event"; it's odd that the API forces you to create a completely new IObservable and then subscribe to that just to get the Finally thing to happen. Plus it allows a potential error (made evident if we use the function in your question) that if you subscribe twice to that new IObservable, your Finally function will execute twice. So you have to make sure that one of your subscriptions is on the "finallied" IObservable and the others are all on the original. Just seems unusual.
I guess the way to think about it is that Finally isn't meant to modify the observable, but rather to modify the subscription itself. i.e., they don't expect you typically to make openly-accessible named observables that have Finally things (var o = Observable.[...].Finally(...);) rather it's meant to go inline with the subscription call itself (var subscription = o.Finally(...).Subscribe(...);)
Related
I have a couple of asynchronous APIs that use callbacks or events instead of async. I successfully used TaskCompletionSource to wrap them as described here.
Now, I would like to use an API that returns IObservable<T> and yields multiple objects. I read about Rx for .NET, which seems the way to go. However, I'm hesitant to include another dependency and another new paradigm, since I'm already using a lot of things that are new for me in this app (like XAML, MVVM, C#'s async/await).
Is there any way to wrap IObservable<T> analogously to how you wrap a single callback API? I would like to call the API as such:
foreach (var t in GetMultipleInstancesAsync()) {
var res = await t;
Console.WriteLine("Received item:", res);
}
If the observable emits multiple values, you can convert them to Task<T> and then add them to any IEnumerable structure.
Check IObservable ToTask. As discussed here, the observable must complete before awaiting otherwise more values might come over.
This guide here might do the trick for you too
public static Task<IList<T>> BufferAllAsync<T>(this IObservable<T> observable)
{
List<T> result = new List<T>();
object gate = new object();
TaskCompletionSource<IList<T>> finalTask = new TaskCompletionSource<IList<T>>();
observable.Subscribe(
value =>
{
lock (gate)
{
result.Add(value);
}
},
exception => finalTask.TrySetException(exception),
() => finalTask.SetResult(result.AsReadOnly())
);
return finalTask.Task;
}
If you would like to use Rx then you can use your returning list:
GetMultipleInstancesAsync().ToObservable().Subscribe(...);
You can subscribe the OnCompleted/OnError handler.
Also you can wrap it a task list:
var result = await Task.WhenAll(GetMultipleInstancesAsync().ToArray());
So you got an array of your results and you are done.
I'm experiencing different behaviour with an TakeUntil and different implementations of IObservable as the parameter. I'm trying to understand WHY i get the different behaviour. Using Linqpad:
async Task Main()
{
var byTimer = true;
var ending = Observable.FromEvent<long>(
handler => endingHandler += handler,
handler => endingHandler -= handler);
var s = new Subject<long>();
using (var disposibleSub =
Observable
.Interval(TimeSpan.FromSeconds(.2))
.TakeUntil(byTimer ? Observable.Timer(TimeSpan.FromSeconds(1.5)) : ending)
.DumpLatest()
.Subscribe(Observer.Checked(s)))
{
if (endingHandler != null)
{
int r = Console.Read();
endingHandler?.Invoke(r);
}
var v = await s.Count();
Console.WriteLine("Count of items: {0}", v);
}
}
public event Action<long> endingHandler;
The one with the timer Count always returns the correct value. However, if I change it to use the FromEvent implementation, I always get 0. Obviously the difference is in the implementation of the two. I've also tried using a Subject implementation for the TakeUntil with the same results as the fromEvent.
The timer result is what I expected.
An explanation why would be appreciated! Thanks.
When byTimer is true then the ending observable never gets a subscription - remember that the observable pipeline is only instantiated when a subscription arrives - so in this case the handler => endingHandler += handler attach event code doesn't run and thus endingHandler is null. That means that the Console.Read() isn't called so the code drops immediately to var v = await s.Count(); and this then catches all of the values passing thru s.
However, when byTimer is false then endingHandler is not null so then Console.Read() is called. When the console is read you immediately invoke endingHandler which stops the observable and calls OnCompleted on the subject. So when it hits var v = await s.Count(); it immediately gets a completed signal, missing all of the previously produced values, and thus you get a count of zero.
If you change your code to be like this:
int r = Console.Read();
if (endingHandler != null)
{
endingHandler?.Invoke(r);
}
var v = await s.Count();
Then the two observables behave exactly the same way.
If you then also go and change the Subject to ReplaySubject then the code should behave as you were originally expecting it to.
I'm looking for a way to implement the following:
A says: "Yo, X happened. Bye."
Others see that and start doing some work.
In other words, I would like to fire an event and let others handle that in a fire and forget way.
So I've looked into the observer pattern: https://msdn.microsoft.com/en-us/library/dd783449(v=vs.110).aspx. However this example is synchronous, and if the observers take a long time to do their work, the notify method blocks for a long time.
I also looked at how to raise events: https://msdn.microsoft.com/en-us/library/9aackb16(v=vs.110).aspx. Also this example is synchronous, and blocks the sender for a long time when the handler takes long to handle the event.
My question is:
How do I do fire and forget events/messages/delegates in C#?
Probably you should meet Task Parallel Library (TPL) Dataflows. There's one data flow called ActionBlock<TInput> that should be a good start for you:
The ActionBlock<TInput> class is a target block that calls a delegate
when it receives data. Think of a ActionBlock<TInput> object as a
delegate that runs asynchronously when data becomes available. The
delegate that you provide to an ActionBlock<TInput> object can be of
type Action or type System.Func<TInput, Task>[...]
Therefore, what about giving a Func<TInput, Task> to ActionBlock<TInput> to perform asynchronous stuff? I've modified the sample found on this TPL Dataflow MSDN article:
List<Func<int, Task>> observers = new List<Func<int, Task>>
{
n => Console.WriteLine(n),
n => Console.WriteLine(n * i),
n => Console.WriteLine(n * n / i)
};
// Create an ActionBlock<int> object that prints values
// to the console.
var actionBlock = new ActionBlock<int>
(
n =>
{
// Fire and forget call to all observers
foreach(Func<int, Task> observer in observers)
{
// Don't await for its completion
observer(n);
}
}
);
// Post several messages to the block.
for (int i = 0; i < 3; i++)
{
actionBlock.Post(i * 10);
}
// Set the block to the completed state
actionBlock.Complete();
// See how I commented out the following sentence.
// You don't wait actions to complete as you want the fire
// and forget behavior!
// actionBlock.Completion.Wait();
You might also want to take a look at BufferBlock<T>.
Consider the following:
[Fact]
public void foo()
{
var result = new Subject<bool>();
var startCount = 0;
var completionCount = 0;
var obs = Observable
.Defer(() =>
{
++startCount;
return result.FirstAsync();
})
.Do(_ => ++completionCount)
.Publish()
.RefCount();
// pretend there are lots of subscribers at once
var s1 = obs.Subscribe();
var s2 = obs.Subscribe();
var s3 = obs.Subscribe();
// even so, we only expect to be started once
Assert.Equal(1, startCount);
Assert.Equal(0, completionCount);
// and we won't complete until the result ticks through
result.OnNext(true);
Assert.Equal(1, startCount);
Assert.Equal(1, completionCount);
s1.Dispose();
s2.Dispose();
s3.Dispose();
// now try exactly the same thing again
s1 = obs.Subscribe();
s2 = obs.Subscribe();
s3 = obs.Subscribe();
// startCount is 4 here instead of the expected 2!
Assert.Equal(2, startCount);
Assert.Equal(1, completionCount);
result.OnNext(true);
Assert.Equal(2, startCount);
Assert.Equal(2, completionCount);
s1.Dispose();
s2.Dispose();
s3.Dispose();
}
My understanding of Publish + RefCount is that a connection to the source is maintained as long as there is at least one subscriber. Once the last subscriber disconnects, any future subscriber will re-initiate the connection to the source.
As you can see in my test, everything works perfectly the first time through. But the second time, the deferred observable inside the pipeline is executed once for every new subscriber.
I can see via the debugger that for the first group of subscribers, obs._count (which counts subscribers) increases for each call to Subscribe. But for the second group of subscribers, it remains zero.
Why is this happening and what can I do to rectify my pipeline?
The answer from #user631090 is close, but incorrect, so I thought I'd answer myself.
It's because Publish will immediately complete new subscribers if the stream it published has itself completed. You can kind of see that in the diagram here:
But it would have been nice if the diagram included a subscriber after the underlying stream completes.
To add to the confusion, Defer is still called for new subscribers. But its return value is simply ignored by Publish because of the initial stream completing.
I'm as yet unable to come up with a way to implement my intended use case. I thought perhaps using Multicast rather than Publish, creating a new subject as necessary. But I haven't been able to achieve that yet. And it seems rather painful for what I would think is a common use case.
It's because the underlying observable result has already completed. So each new subscriber is just getting the OnCompleted callback.
If ObservableDefer was creating a new sequence each time or one that didn't complete you would see the desired behavior.
e.g.
return result.FirstAsync().Concat(Observable.Never<bool>());
You will need to remove the Assert.Equal(1, completionCount);
I have a HashSet. Occasionlay, new values are added to this hashset. What I am trying to do is have a timer remove each element from the set exactly one minute after it was added.
I am still new to rx but this seems like an ideal occasion to use it.
I tried something like this:
AddItem(string item)
{
_mySet.Add(item);
var timer = Observable.Timer(TimeSpan.FromSeconds(60), _scheduler);
timer
.Take(1)
.Do(item => RemoveItem(item))
.Subscribe(_ => Console.WriteLine("Removed {0}", item));
}
It seems to work ok (passes unit tests).
Does anyone see anything wrong with this approach?
Your lambda in the Do call doesn't look right - Observable.Timer produces int values, but your collection is a HashSet<string> - this shouldn't compile. I'm guessing it was just a typo.
Do: in general your subscription should be done in Subscribe. Do is intended for side effects (I dislike the idea of side-effects in a stream so I avoid it but it is useful for debugging).
Take: Observable.Timer only produces one value before it terminates, thus there is no need for the Take operator
I would write your function as:
AddItem(string item)
{
_mySet.Add(item);
Observable.Timer(TimeSpan.FromSeconds(60), _scheduler)
.Subscribe(_ => RemoveItem(item));
}
You dont need to create a sequence to do this. You are already being a good citizen and using a Scheduler explicity, so just use that!
You could just have this for your code
AddItem(string item)
{
_mySet.Add(item);
//Note this does return an IDisposable if you want to cancel the subscription.
_scheduler.Schedule(
TimeSpan.FromSeconds(60),
()=>
{
RemoveItem(item);
Console.WriteLine("Removed {0}", item);
});
}
This basically means there is much less work going on under the covers. Consider all the work the Observable.Timer method is going, when effectively all you just want it to do is schedule an OnNext with a value (that you ignore).
I would also assume that even a user that doesn't know anything about Rx would be able to read this schedule code too. ie. "After I add this item, I schedule this remove action to run in 60 seconds).
If you were using ReactiveUI, a class called ReactiveCollection would definitely help here, you could use it like this:
theCollection.ItemsAdded
.SelectMany(x => Observable.Timer(TimeSpan.FromSeconds(60), _scheduler).Select(_ => x))
.Subscribe(x => theCollection.Remove(x));
Sorry, don't mean to pick on you, but:
ALWAYS DISPOSE IDISPOSABLES!!!!!
(EDIT: Ok, not sure what the heck I put in my coffee this morning, but I answered with a whole mess of nonsense; I'll leave the above only because in general, you do want to make sure to dispose any IDisposable, but in an effort to make up for the babble that follows...)
That call to Subscribe creates a subscription that you are NOT disposing, so multiple calls to this method are just going to queue up more and more crap - now in this specific case, it's not the end of the world since the Timer only fires once, but still...Dispose!
If you really want to use this method (I think a better approach would be to have some running thread/task that "tends" to your values, removing when it thinks its neccesary), at least try something akin to:
Ok, ignore all that struck-out crap. The implementation of Observable.Timer is this:
public static IObservable<long> Timer(TimeSpan dueTime)
{
return s_impl.Timer(dueTime);
}
which in turn calls into this:
public virtual IObservable<long> Timer(TimeSpan dueTime)
{
return Timer_(dueTime, SchedulerDefaults.TimeBasedOperations);
}
which calls...
private static IObservable<long> Timer_(TimeSpan dueTime, IScheduler scheduler)
{
return new Timer(dueTime, null, scheduler);
}
And here's where things get fun - Timer is a Producer<long>, where the meaty bits are:
private IDisposable InvokeStart(IScheduler self, object state)
{
this._pendingTickCount = 1;
SingleAssignmentDisposable disposable = new SingleAssignmentDisposable();
this._periodic = disposable;
disposable.Disposable = self.SchedulePeriodic<long>(1L, this._period, new Func<long, long>(this.Tock));
try
{
base._observer.OnNext(0L);
}
catch (Exception exception)
{
disposable.Dispose();
exception.Throw();
}
if (Interlocked.Decrement(ref this._pendingTickCount) > 0)
{
SingleAssignmentDisposable disposable2 = new SingleAssignmentDisposable {
Disposable = self.Schedule<long>(1L, new Action<long, Action<long>>(this.CatchUp))
};
return new CompositeDisposable(2) { disposable, disposable2 };
}
return disposable;
}
Now, the base._observer.OnNext, that's the internal sink set up to trigger on the timer tick, where the Invoke on that is:
private void Invoke()
{
base._observer.OnNext(0L);
base._observer.OnCompleted();
base.Dispose();
}
So yes. It auto-disposes itself - and there won't be any "lingering subscriptions" floating around.
Mmm....crow is tasty. :|