C# 如何使用RX限制事件流?
我希望有效地限制事件流,以便在接收到第一个事件时调用我的委托,但如果接收到后续事件,则不会在1秒内调用委托。超时(1秒)到期后,如果收到后续事件,我希望调用我的委托 有没有一种简单的方法可以使用反应式扩展来做到这一点 示例代码:C# 如何使用RX限制事件流?,c#,system.reactive,C#,System.reactive,我希望有效地限制事件流,以便在接收到第一个事件时调用我的委托,但如果接收到后续事件,则不会在1秒内调用委托。超时(1秒)到期后,如果收到后续事件,我希望调用我的委托 有没有一种简单的方法可以使用反应式扩展来做到这一点 示例代码: static void Main(string[] args) { Console.WriteLine("Running..."); var generator = Observable .GenerateWithTime(1, x =
static void Main(string[] args)
{
Console.WriteLine("Running...");
var generator = Observable
.GenerateWithTime(1, x => x <= 100, x => x, x => TimeSpan.FromMilliseconds(1), x => x + 1)
.Timestamp();
var builder = new StringBuilder();
generator
.Sample(TimeSpan.FromSeconds(1))
.Finally(() => Console.WriteLine(builder.ToString()))
.Subscribe(feed =>
builder.AppendLine(string.Format("Observed {0:000}, generated at {1}, observed at {2}",
feed.Value,
feed.Timestamp.ToString("mm:ss.fff"),
DateTime.Now.ToString("mm:ss.fff"))));
Console.ReadKey();
}
但我想观察(时间戳显然会改变)
您是否尝试过
油门
扩展方法
从文档中:
忽略可观察序列中的值,这些值后面紧跟在时间之前的另一个值
我不太清楚这是否会达到你想要的效果-因为你想忽略以下值而不是第一个值。。。但我希望这是你想要的。试试看:)
编辑:嗯。。。不,我认为节流阀毕竟是不对的。我相信我看到了您想要做的事情,但在框架中我看不到任何可以做的事情。不过,我可能错过了一些东西。你在Rx论坛上问过吗?如果现在没有,他们很可能会很高兴地加上:)
我想你可以巧妙地用
SkipUntil
和SelectMany
来完成这件事。。。但我认为它应该有自己的方法。您正在搜索的是最组合的
public static IObservable<TResult> CombineLatest<TLeft, TRight, TResult>(
IObservable<TLeft> leftSource,
IObservable<TRight> rightSource,
Func<TLeft, TRight, TResult> selector
)
公共静态IObservable组合测试(
IObservable leftSource,
IObservable rightSource,
函数选择器
)
当选择器(时间)有值时,合并2个obeservable并返回所有值
编辑:约翰是对的,这可能不是首选的解决方案
这里有3种情况:
1) 我希望每秒获得事件流的一个值。
意味着:如果它每秒产生更多的事件,您将得到一个总是更大的缓冲区
observableStream.Throttle(timeSpan)
2) 我想了解最新的事件,那是在第二次事件发生之前制作的
意味着:其他事件被删除
observableStream.Sample(TimeSpan.FromSeconds(1))
3) 你想得到所有发生在最后一秒的事件。每一秒
observableStream.BufferWithTime(timeSpan)
4) 您希望选择在第二个值和所有值之间发生的事情,直到第二个值通过并返回结果
observableStream.CombineLatest(Observable.Interval(1000), selectorOnEachEvent)
好的,这里有一个解决方案。我特别不喜欢,但是。。。哦,好吧 乔恩给我指了指滑雪时,帽子给了我小费,克莱特给了我一次小费。谢谢各位
static void Main(string[] args)
{
Console.WriteLine("Running...");
var generator = Observable
.GenerateWithTime(1, x => x <= 100, x => x, x => TimeSpan.FromMilliseconds(1), x => x + 1)
.Timestamp();
var bufferedAtOneSec = generator.BufferWithTime(TimeSpan.FromSeconds(1));
var action = new Action<Timestamped<int>>(
feed => Console.WriteLine("Observed {0:000}, generated at {1}, observed at {2}",
feed.Value,
feed.Timestamp.ToString("mm:ss.fff"),
DateTime.Now.ToString("mm:ss.fff")));
var reactImmediately = true;
bufferedAtOneSec.Subscribe(list =>
{
if (list.Count == 0)
{
reactImmediately = true;
}
else
{
action(list.Last());
}
});
generator
.SkipWhile(item => reactImmediately == false)
.Subscribe(feed =>
{
if(reactImmediately)
{
reactImmediately = false;
action(feed);
}
});
Console.ReadKey();
}
static void Main(字符串[]args)
{
控制台。WriteLine(“正在运行…”);
无功发生器=可观测
.GenerateWithTime(1,x=>x,x=>TimeSpan.FromMillistics(1),x=>x+1)
.Timestamp();
var bufferedAtOneSec=生成器.BufferWithTime(TimeSpan.FromSeconds(1));
var action=新操作(
feed=>Console.WriteLine(“观察到的{0:000},在{1}生成,在{2}观察到”,
饲料价值,
feed.Timestamp.ToString(“mm:ss.fff”),
DateTime.Now.ToString(“mm:ss.fff”);
var=true;
订阅(列表=>
{
如果(list.Count==0)
{
立即反应=正确;
}
其他的
{
动作(list.Last());
}
});
发电机
.SkipWhile(项=>立即反应==假)
.订阅(订阅源=>
{
如果(立即反应)
{
立即反应=假;
行动(饲料);
}
});
Console.ReadKey();
}
这是我在以下网站上发布的问题答案:
更新:
以下是一个新版本,当发生时差超过1秒的事件时,它不再延迟事件转发:
public static IObservable<T> ThrottleResponsive3<T>(this IObservable<T> source, TimeSpan minInterval)
{
return Observable.CreateWithDisposable<T>(o =>
{
object gate = new Object();
Notification<T> last = null, lastNonTerminal = null;
DateTime referenceTime = DateTime.UtcNow - minInterval;
var delayedReplay = new MutableDisposable();
return new CompositeDisposable(source.Materialize().Subscribe(x =>
{
lock (gate)
{
var elapsed = DateTime.UtcNow - referenceTime;
if (elapsed >= minInterval && delayedReplay.Disposable == null)
{
referenceTime = DateTime.UtcNow;
x.Accept(o);
}
else
{
if (x.Kind == NotificationKind.OnNext)
lastNonTerminal = x;
last = x;
if (delayedReplay.Disposable == null)
{
delayedReplay.Disposable = Scheduler.ThreadPool.Schedule(() =>
{
lock (gate)
{
referenceTime = DateTime.UtcNow;
if (lastNonTerminal != null && lastNonTerminal != last)
lastNonTerminal.Accept(o);
last.Accept(o);
last = lastNonTerminal = null;
delayedReplay.Disposable = null;
}
}, minInterval - elapsed);
}
}
}
}), delayedReplay);
});
}
publicstaticiobservable ThrottleResponsive3(此IObservable源,TimeSpan minInterval)
{
返回可观察的。CreateWithDisposable(o=>
{
对象门=新对象();
通知last=null,lastnotterminal=null;
DateTime referenceTime=DateTime.UtcNow-minInterval;
var delayedReplay=new MutableDisposable();
返回新的CompositeDisposable(source.Materialize().Subscribe(x=>
{
锁(门)
{
var exposed=DateTime.UtcNow-referenceTime;
if(已用时间>=minInterval&&delayedReplay.Disposable==null)
{
referenceTime=DateTime.UtcNow;
x、 接受(o);
}
其他的
{
if(x.Kind==NotificationKind.OnNext)
lastNonTerminal=x;
last=x;
if(delayedReplay.Disposable==null)
{
delayedReplay.Dispossible=Scheduler.ThreadPool.Schedule(()=>
{
锁(门)
{
referenceTime=DateTime.UtcNow;
if(lastnotterminal!=null&&lastnotterminal!=last)
lastnotterminal.Accept(o);
最后,接受(o);
last=lastNonTerminal=null;
delayedReplay.Disposable=null;
}
},最小间隔-已过);
}
}
}
}),delayedRepla
static void Main(string[] args)
{
Console.WriteLine("Running...");
var generator = Observable
.GenerateWithTime(1, x => x <= 100, x => x, x => TimeSpan.FromMilliseconds(1), x => x + 1)
.Timestamp();
var bufferedAtOneSec = generator.BufferWithTime(TimeSpan.FromSeconds(1));
var action = new Action<Timestamped<int>>(
feed => Console.WriteLine("Observed {0:000}, generated at {1}, observed at {2}",
feed.Value,
feed.Timestamp.ToString("mm:ss.fff"),
DateTime.Now.ToString("mm:ss.fff")));
var reactImmediately = true;
bufferedAtOneSec.Subscribe(list =>
{
if (list.Count == 0)
{
reactImmediately = true;
}
else
{
action(list.Last());
}
});
generator
.SkipWhile(item => reactImmediately == false)
.Subscribe(feed =>
{
if(reactImmediately)
{
reactImmediately = false;
action(feed);
}
});
Console.ReadKey();
}
public static IObservable<T> ThrottleResponsive3<T>(this IObservable<T> source, TimeSpan minInterval)
{
return Observable.CreateWithDisposable<T>(o =>
{
object gate = new Object();
Notification<T> last = null, lastNonTerminal = null;
DateTime referenceTime = DateTime.UtcNow - minInterval;
var delayedReplay = new MutableDisposable();
return new CompositeDisposable(source.Materialize().Subscribe(x =>
{
lock (gate)
{
var elapsed = DateTime.UtcNow - referenceTime;
if (elapsed >= minInterval && delayedReplay.Disposable == null)
{
referenceTime = DateTime.UtcNow;
x.Accept(o);
}
else
{
if (x.Kind == NotificationKind.OnNext)
lastNonTerminal = x;
last = x;
if (delayedReplay.Disposable == null)
{
delayedReplay.Disposable = Scheduler.ThreadPool.Schedule(() =>
{
lock (gate)
{
referenceTime = DateTime.UtcNow;
if (lastNonTerminal != null && lastNonTerminal != last)
lastNonTerminal.Accept(o);
last.Accept(o);
last = lastNonTerminal = null;
delayedReplay.Disposable = null;
}
}, minInterval - elapsed);
}
}
}
}), delayedReplay);
});
}
var source = Observable.GenerateWithTime(1,
x => x <= 100, x => x, x => TimeSpan.FromMilliseconds(1), x => x + 1)
.Timestamp();
source.Publish(o =>
o.Take(1).Merge(o.Skip(1).Sample(TimeSpan.FromSeconds(1)))
).Run(x => Console.WriteLine(x));
public static IObservable<T> SampleResponsive<T>(
this IObservable<T> source, TimeSpan delay)
{
return source.Publish(src =>
{
var fire = new Subject<T>();
var whenCanFire = fire
.Select(u => new Unit())
.Delay(delay)
.StartWith(new Unit());
var subscription = src
.CombineVeryLatest(whenCanFire, (x, flag) => x)
.Subscribe(fire);
return fire.Finally(subscription.Dispose);
});
}
public static IObservable<TResult> CombineVeryLatest
<TLeft, TRight, TResult>(this IObservable<TLeft> leftSource,
IObservable<TRight> rightSource, Func<TLeft, TRight, TResult> selector)
{
var ls = leftSource.Select(x => new Used<TLeft>(x));
var rs = rightSource.Select(x => new Used<TRight>(x));
var cmb = ls.CombineLatest(rs, (x, y) => new { x, y });
var fltCmb = cmb
.Where(a => !(a.x.IsUsed || a.y.IsUsed))
.Do(a => { a.x.IsUsed = true; a.y.IsUsed = true; });
return fltCmb.Select(a => selector(a.x.Value, a.y.Value));
}
private class Used<T>
{
internal T Value { get; private set; }
internal bool IsUsed { get; set; }
internal Used(T value)
{
Value = value;
}
}
public static IObservable<TResult> CombineVeryLatest
<TLeft, TRight, TResult>(this IObservable<TLeft> leftSource,
IObservable<TRight> rightSource, Func<TLeft, TRight, TResult> selector)
{
return Observable.Defer(() =>
{
int l = -1, r = -1;
return Observable.CombineLatest(
leftSource.Select(Tuple.Create<TLeft, int>),
rightSource.Select(Tuple.Create<TRight, int>),
(x, y) => new { x, y })
.Where(t => t.x.Item2 != l && t.y.Item2 != r)
.Do(t => { l = t.x.Item2; r = t.y.Item2; })
.Select(t => selector(t.x.Item1, t.y.Item1));
});
}
var delay = Observable.Empty<T>().Delay(TimeSpan.FromSeconds(1));
var throttledSource = source.Take(1).Concat(delay).Repeat();
class Sample<T> {
public Sample(T lastValue, Int32 count) {
LastValue = lastValue;
Count = count;
}
public T LastValue { get; private set; }
public Int32 Count { get; private set; }
}
public static IObservable<Sample<T>> SampleResponsive<T>(this IObservable<T> source, TimeSpan interval, IScheduler scheduler = null) {
if (source == null)
throw new ArgumentNullException(nameof(source));
return Observable.Create<Sample<T>>(
observer => {
var gate = new Object();
var lastSampleValue = default(T);
var lastSampleTime = default(DateTime);
var sampleCount = 0;
var scheduledTask = new SerialDisposable();
return new CompositeDisposable(
source.Subscribe(
value => {
lock (gate) {
var now = DateTime.UtcNow;
var elapsed = now - lastSampleTime;
if (elapsed >= interval) {
observer.OnNext(new Sample<T>(value, 1));
lastSampleValue = value;
lastSampleTime = now;
sampleCount = 0;
}
else {
if (scheduledTask.Disposable == null) {
scheduledTask.Disposable = (scheduler ?? Scheduler.Default).Schedule(
interval - elapsed,
() => {
lock (gate) {
if (sampleCount > 0) {
lastSampleTime = DateTime.UtcNow;
observer.OnNext(new Sample<T>(lastSampleValue, sampleCount));
sampleCount = 0;
}
scheduledTask.Disposable = null;
}
}
);
}
lastSampleValue = value;
sampleCount += 1;
}
}
},
error => {
if (sampleCount > 0)
observer.OnNext(new Sample<T>(lastSampleValue, sampleCount));
observer.OnError(error);
},
() => {
if (sampleCount > 0)
observer.OnNext(new Sample<T>(lastSampleValue, sampleCount));
observer.OnCompleted();
}
),
scheduledTask
);
}
);
}