我正在尝试使IObservable<bool>
返回true
如果在最后5秒内收到UDP消息,并且如果发生超时,则返回false。
到目前为止,我有这个:
public IObservable<Boolean> GettingUDPMessages(IPEndPoint localEP)
{
var udp = BaseComms.UDPBaseStringListener(localEP)
.Where(msg => msg.Data.Contains("running"))
.Select(s => true);
return Observable
.Timeout(udp, TimeSpan.FromSeconds(5))
.Catch(Observable.Return(false));
}
问题是: -
true
或false
进行状态更改。我可以使用Subject<T>
但是当没有更多订阅者时我需要小心处理UDPBaseStringListener
observable。
更新
每当我收到UDP消息时,我希望它返回true
。如果我在过去5秒内没有收到UDP消息,我希望它返回false
。
答案 0 :(得分:3)
正如Bj0所指出的,带有BufferWithTime
的解决方案在收到数据点后不会立即返回数据点,并且在收到数据点后未重置缓冲区超时。
使用Rx Extensions 2.0,您可以解决新缓冲区重载同时接受超时和大小的问题:
static IObservable<Boolean> GettingUDPMessages(IPEndPoint localEP)
{
return BaseComms
.UDPBaseStringListener(localEP)
.Where(msg => msg.Data.Contains("running"))
.Buffer(TimeSpan.FromSeconds(5), 1)
.Select(s => s.Count > 0)
.DistinctUntilChanged();
}
答案 1 :(得分:2)
缓冲区的问题是,当你获得一个新值时,“超时”间隔不会被重置,缓冲区窗口只是相互跟随的时间片段(在这种情况下是5秒)。这意味着,根据您收到最后一个值的时间,您可能需要等待几乎两倍的超时值。这也可能会错过超时:
should timeout here
v
0s 5s 10s 15s
|x - x - x | x - - - - | - - - x -| ...
true true true
但是,IObservable.Throttle每次进入新值时都会自行重置,并且只在经过了一段时间后产生一个值(最后一个传入值)。这可以用作超时并与IObservable合并,以将“超时”值插入流中:
var obs = BaseComms.UDPBaseStringListener(localEP)
.Where(msg => msg.Data.Contains("running"));
return obs.Merge( obs.Throttle( TimeSpan.FromSeconds(5) )
.Select( x => false ) )
.DistinctUntilChanged();
一个有效的LINQPad示例:
var sub = new Subject<int>();
var script = sub.Timestamp()
.Merge( sub.Throttle(TimeSpan.FromSeconds(2)).Select( i => -1).Timestamp())
.Subscribe( x =>
{
x.Dump("val");
});
Thread.Sleep(1000);
sub.OnNext(1);
sub.OnNext(2);
Thread.Sleep(10000);
sub.OnNext(5);
在2秒超时后,将-1插入到流中。
答案 2 :(得分:1)
我建议避免使用Timeout
- 它会导致异常,而异常编码则很糟糕。
此外,似乎只有你的观察值在一个值之后停止才有意义。您可能需要更多地解释您希望行为的内容。
我目前解决您的问题的方法是:
public IObservable<Boolean> GettingUDPMessages(IPEndPoint localEP)
{
return Observable.Create<bool>(o =>
{
var subject = new AsyncSubject<bool>();
return new CompositeDisposable(
Observable.Amb(
BaseComms
.UDPBaseStringListener(localEP)
.Where(msg => msg.Data.Contains("running"))
.Select(s => true),
Observable
.Timer(TimeSpan.FromMilliseconds(10.0))
.Select(_ => false)
).Take(1).Subscribe(subject), subject.Subscribe(o));
});
}
这有帮助吗?
答案 3 :(得分:1)
如果您不希望序列停止,只需将其包裹在延迟+重复:
Observable.Defer(() => GettingUDPMessages(endpoint)
.Repeat();
答案 4 :(得分:0)
根据答案 here,我创建了以下扩展方法:
/// <summary>
/// Emits a value every interval until a value is emitted on the TakeUntil observable. The TakeUntil and Repeat
/// operators work together to create a resetting timer that is reset every time a value is emitted on the
/// TakeUntil observable.
/// </summary>
/// <param name="observable">The observable to observe for timeouts</param>
/// <param name="timeout">The value to use to emit a timeout value</param>
/// <param name="timeoutValue">The value to emit when the observable times out</param>
public static IObservable<T> SelectTimeout<T>(this IObservable<T> observable, TimeSpan timeout, T timeoutValue)
{
IObservable<T> timeoutObservable = Observable
.Interval(timeout)
.Select(_ => timeoutValue)
.TakeUntil(observable)
.Repeat();
return observable.Merge(timeoutObservable);
}
您将需要以下 using
指令:
using System;
using System.Reactive.Linq;
请注意,每次超时后都会发出超时值。如果您只想发出一个超时值,您可以使用 DistinctUntilChanged
方法或查看链接的答案。我更喜欢不断发出超时,因为它更简单、更容易理解。