【问题标题】:Reactive subscription to event not followed by different event within a period of time对事件的反应性订阅在一段时间内没有跟随不同的事件
【发布时间】:2013-02-08 23:02:16
【问题描述】:

我正在尝试弄清楚如何为在特定时间窗口内没有跟随不同事件的事件创建响应式订阅。

为了说明,这里有一个用例:

由两个事件触发的繁忙指示器。忙和不忙。它们可能会一起开火,但指示灯不应频繁闪烁。

当 NotBusy 触发时,应该没有指示器。当 Busy 触发且 NotBusy 在 5 秒内未触发时,它应该会显示。

有没有办法完全在 Reactive 内完成此操作而无需添加外部状态?

【问题讨论】:

标签: c# events system.reactive


【解决方案1】:

啊,我无法抗拒 - 你多久反击一次源材料的作者? (参见 Benjol 对问题的评论) :)

这是我的尝试(LINQPad 就绪):

输出如下所示:

At 1/1/0001 12:00:00 AM +00:00, Busy Signal Indicator is now:OFF
Sending BUSY at 1/1/0001 12:00:00 AM +00:00
Sending NOTBUSY at 1/1/0001 12:00:02 AM +00:00

Sending BUSY at 1/1/0001 12:00:03 AM +00:00
Sending BUSY at 1/1/0001 12:00:06 AM +00:00
At 1/1/0001 12:00:08 AM +00:00, Busy Signal Indicator is now:ON
Sending NOTBUSY at 1/1/0001 12:00:09 AM +00:00
At 1/1/0001 12:00:09 AM +00:00, Busy Signal Indicator is now:OFF

这里是我们事件的基本定义:

enum EventType 
{ 
    Busy, 
    NotBusy 
}
class StreamEvent 
{ 
     public EventType Type {get; set;} 
     public StreamEvent(EventType type) { Type = type;}
}

这是查询+测试代码:

void Main()
{
    // our simulated event stream
    var fakeSource = new System.Reactive.Subjects.Subject<StreamEvent>();

    // Let's use a scheduler we actually don't have to wait for
    var theTardis = new System.Reactive.Concurrency.HistoricalScheduler();

    var busySignal = fakeSource
        // Batch up events:
        .Window(
            // Starting batching on a busy signal
            fakeSource.Where(e => e.Type == EventType.Busy),
            // Stop batching on a not busy signal
            (open) => fakeSource.Where(e => e.Type == EventType.NotBusy)
                // but throw a timeout if we exceed 5 seconds per window
                .Timeout(TimeSpan.FromSeconds(5), theTardis))       
        // Unpack the windows
        .Switch()
        // Catch any timeout exception and inject a NULL into the stream        
        .Catch(fakeSource.StartWith((StreamEvent)null))
        // Bool-ify on "did a timeout happen?"
        .Select(e => e == null)
        // Start in an "unbusy" state
        .StartWith(false)
        // And only tell us about transitions
        .DistinctUntilChanged();    

    using(busySignal.Subscribe(signal => 
        Console.WriteLine("At {0}, Busy Signal Indicator is now:{1}",
            theTardis.Now,
            signal ? "ON" : "OFF")))
    {
        // should not generate a busy signal
        Console.WriteLine("Sending BUSY at {0}", theTardis.Now);
        fakeSource.OnNext(new StreamEvent(EventType.Busy));
        theTardis.AdvanceBy(TimeSpan.FromSeconds(2));
        Console.WriteLine("Sending NOTBUSY at {0}", theTardis.Now);
        fakeSource.OnNext(new StreamEvent(EventType.NotBusy));
        theTardis.AdvanceBy(TimeSpan.FromSeconds(1));
        Console.WriteLine();

        // should generate a busy signal
        Console.WriteLine("Sending BUSY at {0}", theTardis.Now);
        fakeSource.OnNext(new StreamEvent(EventType.Busy));
        theTardis.AdvanceBy(TimeSpan.FromSeconds(3));
        Console.WriteLine("Sending BUSY at {0}", theTardis.Now);
        fakeSource.OnNext(new StreamEvent(EventType.Busy));
        theTardis.AdvanceBy(TimeSpan.FromSeconds(3));

        // and this should clear it
        Console.WriteLine("Sending NOTBUSY at {0}", theTardis.Now);
        fakeSource.OnNext(new StreamEvent(EventType.NotBusy));      
        theTardis.AdvanceBy(TimeSpan.FromSeconds(1));
        Console.WriteLine();    
    }
}

【讨论】:

  • @JayWalker 您需要添加 Rx 程序集(您可以在最新的 LINQPad 中使用 Nuget),并添加以下用法:System.Reactive.Subjects System.Reactive.Concurrency
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-17
  • 1970-01-01
  • 1970-01-01
  • 2017-01-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多