使用反应性延伸的心跳模式

时间:2017-02-27 17:42:31

标签: c# system.reactive .net-4.6 .net-4.6.1

给出一个简单的场景:

A和B在一个房间里,A和B说话。房间很暗,B看不到A. B怎么能弄清楚A是暂停还是A被绑架了?

当A谈话时,A提供IObservable Talk ,B随后订阅了Talk.Subscribe(string =>处理A所说的)。 B可以同时订阅Observable.Interval Heartbeat 作为心跳检查。

我的问题是我应该使用什么操作符来合并/组合两个IObservable,这样如果 Talk 中没有来自两个 Heartbeat 的项目,B将假定A被绑架了。

请注意,我想避免使用变量来存储状态,因为如果我没有正确地同步该变量,可能会导致副作用。

谢谢,

1 个答案:

答案 0 :(得分:2)

想象一下你想要采取行动的状态变量,状态代表心跳的数量,因为' A'上次发言这看起来像这样:

var stateObservable = Observable.Merge(                     //State represent number of heartbeats since A last spoke
    aSource.Select(_ => new Func<int, int>(i => 0)),        //When a talks, set state to 0
    bHeartbeat.Select(_ => new Func<int, int>(i => i + 1))  //when b heartbeats, increment state
)
    .Scan(0, (state, func) => func(state));

我们将A口语的事件表示为将状态重置为0的功能,以及将B心跳作为递增状态的事件。然后我们使用Scan函数累积。

其余的很容易:

var isKidnapped = stateObservable
    .Where(state => state >= 2)
    .Take(1);

isKidnapped.Subscribe(_ => Console.WriteLine("A is kidnapped"));

修改

以下是 n A来源的示例:

var aSources = new Subject<Tuple<string, Subject<string>>>();
var bHeartbeat = Observable.Interval(TimeSpan.FromSeconds(1)).Publish().RefCount();

var stateObservable = aSources.SelectMany(t =>
        Observable.Merge(
            t.Item2.Select(_ => new Func<int, int>(i => 0)),
            bHeartbeat.Select(_ => new Func<int, int>(i => i + 1))
        )
        .Scan(0, (state, func) => func(state))
        .Where(state => state >= 2)
        .Take(1)
        .Select(_ => t.Item1)
    );

stateObservable.Subscribe(s => Console.WriteLine($"{s} is kidnapped"));
aSources
    .SelectMany(t => t.Item2.Select(s => Tuple.Create(t.Item1, s)))
    .Subscribe(t => Console.WriteLine($"{t.Item1} says '{t.Item2}'"));
bHeartbeat.Subscribe(_ => Console.WriteLine("**Heartbeat**"));

var a = new Subject<string>();
var c = new Subject<string>();
var d = new Subject<string>();
var e = new Subject<string>();
var f = new Subject<string>();

aSources.OnNext(Tuple.Create("A", a));
aSources.OnNext(Tuple.Create("C", c));
aSources.OnNext(Tuple.Create("D", d));
aSources.OnNext(Tuple.Create("E", e));
aSources.OnNext(Tuple.Create("F", f));

a.OnNext("Hello");
c.OnNext("My name is C");
d.OnNext("D is for Dog");
await Task.Delay(TimeSpan.FromMilliseconds(1200));
e.OnNext("Easy-E here");
a.OnNext("A is for Apple");
await Task.Delay(TimeSpan.FromMilliseconds(2200));