问题
I have code similar to this.
IPruduceDemUpdates.Subscribe(update => DoUpdate(update));
But what I want to do is something like that.
IPruduceDemUpdates.Subscribe(update => if(NoDoUpadteIsRunning) DoUpdate(update));
So it ignores incoming updates, if the update method is already running. In addition, it should always execute the last update. No matter if it is the last update of the stream or the last for a period of time. Here an example timeline
- Update 1 starts
- Update 2 is ignored
- Update 3 is ignored
- Update 4 is ignored
- Update 1 finished
- Update 4 starts
- Update 4 finished
Edit
I have solution for skipping
IPruduceDemUpdates.Subscribe(update =>
{
if (_task == null || _task.IsCompleted || _task.IsCanceled || _task.IsFaulted)
_task = DoUpdate(update);
});
But I don't know how to be sure, that the last update will process.
回答1:
If DoUpdate
is synchronous (which it appears to be in this case), you can use BufferIntrospective
from Rxx. It does exactly what you want:
IProduceDemUpdates
.BufferIntrospective()
.Where(items => items.Count > 0) // ignore empty buffers
.Select(items => items[items.Count - 1]) // ignore all but last item in buffer
.Subscribe(DoUpdate);
回答2:
I'm a bit late to the party, but here's built-in one-liner way to do this. Sample
with a TimeSpan.Zero
will continuously sample the source and always push the last update - to make this work you need to give Sample
it's own thread so that the sampled source can run uninterrupted:
IProduceDemUpdates.Sample(TimeSpan.Zero, NewThreadScheduler.Default)
.Subscribe(DoUpdate);
Notes
The thread is reused per subscriber to Sample
not generated per event - so no need to worry about a mountain of threads being created. You could also do .Sample(TimeSpan.Zero, new EventLoopScheduler())
. The point is that the subscriber is being delivered events on the same thread that Sample
is sampling on, so that Sample
won't sample the next event until the subscriber is done.
Examples
The following code:
// emits 0,1,2,3,4,5,6,7,8,9
var source = Observable.Interval(TimeSpan.FromSeconds(1)).Take(10);
source.Sample(TimeSpan.Zero, NewThreadScheduler.Default).Subscribe(x => {
Console.WriteLine(x);
Task.Delay(TimeSpan.FromSeconds(3)).Wait();
});
Will typically output:
0
2
5
9
To convince yourself the last update is always received try:
// emit 0,1,2
var source = Observable.Interval(TimeSpan.FromSeconds(1)).Take(3);
source.Sample(TimeSpan.Zero, NewThreadScheduler.Default).Subscribe(x => {
Console.WriteLine(x);
Task.Delay(TimeSpan.FromSeconds(10)).Wait();
});
Which will output:
0
2
Caveat
Whatever your approach, note how important it is that you accomplish the slowest operation in the synchronous chain of subscribers attached to Sample
. If you have asynchronous subscriptions further downstream (such as an ObserveOn
) that include slower operators then you will just be transferring the back-pressure elsewhere.
来源:https://stackoverflow.com/questions/26623988/ignore-incoming-stream-updates-if-last-callback-hasnt-finished-yet