在IObservable上检测IsAlive

时间:2011-05-28 16:31:48

标签: system.reactive code-golf

我正在编写一个函数IsAlive来获取IObservable<T>和一个时间跨度,并返回一个IObservable<bool>规范用例是检测流服务器是否仍在发送数据

我已经为它提出了以下解决方案,但感觉它的工作原理并不是最明确的。

public static IObservable<bool> IsAlive<T>(this IObservable<T> source, 
                                           TimeSpan timeout, 
                                           IScheduler sched)
{
    return source.Window(timeout, sched)
                 .Select(wind => wind.Any())
                 .SelectMany(a => a)
                 .DistinctUntilChanged();
}

有没有人有更好的方法?

仅供参考 - 以下是我尝试过的单元测试和现有方法:https://gist.github.com/997003

2 个答案:

答案 0 :(得分:1)

怎么样:

source.Select(_ => true)
    .Timeout(timeout, sched)
    .DistinctUntilChanged()
    .Catch<bool, TimeoutException>)(ex => Observable.Return(false));

答案 1 :(得分:1)

这应该有效:

public static IObservable<bool> IsAlive<T>(this IObservable<T> source, 
                                           TimeSpan timeout, 
                                           IScheduler sched)
{
    return source.Buffer(timeout, 1, sched)
                 .Select(l => l.Any())
                 .DistinctUntilChanged();
}

这种方法也具有语义意义。每次项目进入时,它都会填充缓冲区,然后传递true。每次超时,都会创建一个空缓冲区,并传递false。

修改

这就是缓冲区1方法优于窗口化的原因:

var sched = new TestScheduler();
var subj = new Subject<Unit>();

var timeout = TimeSpan.FromTicks(10);

subj
    .Buffer(timeout, 1, sched)
    .Select(Enumerable.Any)
    .Subscribe(x => Console.WriteLine("Buffer(timeout, 1): " + x));

subj
    .Window(timeout, sched)
    .Select(wind => wind.Any())
    .SelectMany(a => a)
    .Subscribe(x => Console.WriteLine("Window(timeout): "+x));

sched.AdvanceTo(5);
subj.OnNext(Unit.Default);
sched.AdvanceTo(16);

的产率:

Buffer(timeout, 1): True
Window(timeout): True
Buffer(timeout, 1): False

具体来说,窗口在整个超时时间内打开,并且一旦项目进入就不会关闭和重置。这是缓冲区限制为1的地方。一旦项目进入,缓冲区及其计时器就会重新启动。

我可以将缓冲区重新实现为一个窗口,因为缓冲区的实现一个窗口,但是a)我认为缓冲区具有更好的语义感,而b)我没有SelectMany。 Scott的Select和SelectMany可以组合成一个SelectMany(x =&gt; x.Any()),但是我可以避免整个lambda并指定Enumerable.Any方法组,无论如何它将更快地绑定(平凡)。