C# 如何使用Rx.Net处理可能是突发事件或垃圾邮件的事件?

C# 如何使用Rx.Net处理可能是突发事件或垃圾邮件的事件?,c#,system.reactive,C#,System.reactive,我正在编写代码来处理来自设备的事件,在这些设备中,所述事件可能以突发、常见的情况发生,或者在错误情况下可能非常频繁地发生 我想要的行为是: 在给定的时间窗口内,事件转发不得超过一次。(1分钟) 在一般情况下,不要分割事件突发(通常是几秒钟),在转发前等待安静时间 在错误情况下,如果正在生成事件,但在2倍的时间窗口内未转发任何事件,则转发最后一个事件 我发现节流阀以我想要的方式工作,等待一段时间,然后发送最后一个事件。但是,如果事件是垃圾邮件,Throttle永远不会转发任何事件,因为静默期窗

我正在编写代码来处理来自设备的事件,在这些设备中,所述事件可能以突发、常见的情况发生,或者在错误情况下可能非常频繁地发生

我想要的行为是:

  • 在给定的时间窗口内,事件转发不得超过一次。(1分钟)
  • 在一般情况下,不要分割事件突发(通常是几秒钟),在转发前等待安静时间
  • 在错误情况下,如果正在生成事件,但在2倍的时间窗口内未转发任何事件,则转发最后一个事件
我发现节流阀以我想要的方式工作,等待一段时间,然后发送最后一个事件。但是,如果事件是垃圾邮件,Throttle永远不会转发任何事件,因为静默期窗口会重复重置

我发现样本工作得很好,除了当一个时间窗口结束时发生突发事件,因为我对突发中发生的事件不感兴趣。 我知道使用Switch或Join很可能可以解决这个问题,但我还没有找到一个与我的场景足够接近的示例来让它适合我

using System;
using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Reactive.Concurrency;
using System.Diagnostics;

public class Program
{
    public static event EventHandler<FakeEventArgs> DeviceChange;
    public static TimeSpan window = TimeSpan.FromSeconds(60);
    public static uint eventCounter = 0;

    public static async Task Main()
    {
        var window2x = window + window;
        //Just to give a visual sense of when things are happening
        Observable.Interval(window).Subscribe(iterator => Console.WriteLine($"Non-sliding Window {iterator}"));

        //Create observable from standard event in order to use Rx.
        var eventsAsObservables = Observable.FromEventPattern<FakeEventArgs>
            (
                handler => DeviceChange += handler,
                handler => DeviceChange -= handler
            );

        //pure throttle doesn't work in the case where events always firing faster than the time window (i.e. device with faulty connection)
        //eventsAsObservables
        //  .Throttle(window)
        //  .ObserveOn(ThreadPoolScheduler.Instance)
        //  .Subscribe(evt => { var now = DateTime.Now; Console.WriteLine($"Event: {evt.EventArgs.Message} Sent at: {evt.EventArgs.Created.TimeOfDay}  Handled at: {now.TimeOfDay} Elapsed: {(now - evt.EventArgs.Created).TotalSeconds}"); });

        //pure sample doesn't work in the case where clusters of events are happening across the time window boundary (i.e. device unplugged right at time window)
        //eventsAsObservables
        //  .Sample(window)
        //  .ObserveOn(ThreadPoolScheduler.Instance)
        //  .Subscribe(evt => { var now = DateTime.Now; Console.WriteLine($"Event: {evt.EventArgs.Message} Sent at: {evt.EventArgs.Created.TimeOfDay}  Handled at: {now.TimeOfDay} Elapsed: {(now - evt.EventArgs.Created).TotalSeconds}"); });

        var throttled = eventsAsObservables.Throttle(window);
        var sampled = eventsAsObservables.Sample(window2x);

        //plain merge will forward extra events to subscribers
        //throttled
        //  .Merge(sampled)
        //  .ObserveOn(ThreadPoolScheduler.Instance)
        //  .Subscribe(evt => { var now = DateTime.Now; Console.WriteLine($"Event: {evt.EventArgs.Message} Sent at: {evt.EventArgs.Created.TimeOfDay}  Handled at: {now.TimeOfDay} Elapsed: {(now - evt.EventArgs.Created).TotalSeconds}"); });

        //How do I alter this to get the desired behavior?
        throttled
            .Select(selector => sampled)
            .Switch()
            .ObserveOn(ThreadPoolScheduler.Instance)
            .Subscribe(evt => { var now = DateTime.Now; Console.WriteLine($"Event: {evt.EventArgs.Message} Sent at: {evt.EventArgs.Created.TimeOfDay}  Handled at: {now.TimeOfDay} Elapsed: {(now - evt.EventArgs.Created).TotalSeconds}"); });

        Console.WriteLine($"About to start raising events {DateTime.Now}");
        //RaiseEvent($"{++eventCounter}");

        //These events occur very frequently
        //They cause Throttle to never forward anything because the quiet timer gets reset
        StartSpammyEventsAsync(100);

        //These events will burst on the time boundary 
        //Causes Throttle to never forward event because the quiet timer gets reset just before it expires
        //Causes Sample to forward event from the middle of the burst instead of the end
        StartBurstyEventsAsync(window);

        Console.WriteLine("\nPress ENTER to exit...\n");
        Console.ReadLine();
    }

    static void RaiseEvent(string eventedMessage) =>
        DeviceChange?.Invoke(null, new FakeEventArgs(eventedMessage));

    static async Task StartSpammyEventsAsync(int milliSeconds)
    {
        while (true)
        {
            await Task.Delay(milliSeconds).ConfigureAwait(false);
            Debug.WriteLine($"Raising event {eventCounter}");
            RaiseEvent($"{++eventCounter}");
        }
    }

    static async Task StartBurstyEventsAsync(TimeSpan window)
    {
        while (true)
        {
            await Task.Delay(window - TimeSpan.FromSeconds(1)).ConfigureAwait(false);

            //two second burst of events
            var start = DateTime.Now;
            var limit = TimeSpan.FromSeconds(2);
            while (DateTime.Now - start < limit)
            {
                await Task.Delay(100).ConfigureAwait(false);
                Debug.WriteLine($"Raising event {eventCounter}");
                RaiseEvent($"{++eventCounter}");
            }
        }
    }

    public class FakeEventArgs : EventArgs
    {
        public readonly string Message;
        public readonly DateTime Created;

        protected FakeEventArgs() { }

        public FakeEventArgs(string message):
            base()
        {
            Created = DateTime.Now;
            Message = message;
        }
    }
} 
使用系统;
使用System.Reactive.Linq;
使用System.Threading.Tasks;
使用System.Reactive.Concurrency;
使用系统诊断;
公共课程
{
公共静态事件处理程序DeviceChange;
公共静态TimeSpan窗口=TimeSpan.FromSeconds(60);
公共静态uint事件计数器=0;
公共静态异步任务Main()
{
var window2x=窗口+窗口;
//只是为了让人们直观地了解事情发生的时间
Observable.Interval(window.Subscribe)(迭代器=>Console.WriteLine($“非滑动窗口{iterator}”);
//从标准事件中创建可观察事件,以便使用Rx。
var eventsAsObservables=Observable.FromEventPattern
(
handler=>DeviceChange+=handler,
处理程序=>DeviceChange-=处理程序
);
//纯油门在事件总是以比时间窗口更快的速度触发的情况下不起作用(即设备连接故障)
//可观察事件
//.节气门(车窗)
//.ObserveOn(ThreadPoolScheduler.Instance)
//.Subscribe(evt=>{var now=DateTime.now;Console.WriteLine($“事件:{evt.EventArgs.Message}发送地点:{evt.EventArgs.Created.TimeOfDay}处理地点:{now.TimeOfDay}经过:{(now-evt.EventArgs.Created).TotalSeconds}”);
//pure sample不适用于跨时间窗口边界发生事件集群的情况(即设备在时间窗口处被拔出)
//可观察事件
//.样本(窗口)
//.ObserveOn(ThreadPoolScheduler.Instance)
//.Subscribe(evt=>{var now=DateTime.now;Console.WriteLine($“事件:{evt.EventArgs.Message}发送地点:{evt.EventArgs.Created.TimeOfDay}处理地点:{now.TimeOfDay}经过:{(now-evt.EventArgs.Created).TotalSeconds}”);
var throttled=事件可观察到的事件。Throttle(窗口);
var sampled=eventsAsObservables.Sample(window2x);
//普通合并将向订阅服务器转发额外事件
//节流
//.合并(抽样)
//.ObserveOn(ThreadPoolScheduler.Instance)
//.Subscribe(evt=>{var now=DateTime.now;Console.WriteLine($“事件:{evt.EventArgs.Message}发送地点:{evt.EventArgs.Created.TimeOfDay}处理地点:{now.TimeOfDay}经过:{(now-evt.EventArgs.Created).TotalSeconds}”);
//如何更改此选项以获得所需的行为?
节流
.选择(选择器=>采样)
.Switch()
.ObserveOn(ThreadPoolScheduler.Instance)
.Subscribe(evt=>{var now=DateTime.now;Console.WriteLine($“事件:{evt.EventArgs.Message}发送地点:{evt.EventArgs.Created.TimeOfDay}处理地点:{now.TimeOfDay}经过:{(now-evt.EventArgs.Created).TotalSeconds}”);
WriteLine($“即将开始引发事件{DateTime.Now}”);
//RaiseEvent($“{++eventCounter}”);
//这些事件经常发生
//它们导致油门从不前进,因为安静计时器被重置
StartSpammyEventsAsync(100);
//这些事件将在时间边界上爆发
//导致油门从不向前事件,因为安静计时器在其到期前重置
//使样本从突发的中间而不是结束转发事件
StartBurstyEventsAsync(窗口);
Console.WriteLine(“\n按ENTER退出…\n”);
Console.ReadLine();
}
静态void RaiseEvent(string eventedMessage)=>
DeviceChange?.Invoke(null,新FakeEventArgs(eventedMessage));
静态异步任务StartSpammyEventsAsync(整数毫秒)
{
while(true)
{
等待任务。延迟(毫秒)。配置等待(false);
WriteLine($“引发事件{eventCounter}”);
RaiseEvent($“{++eventCounter}”);
}
}
静态异步任务StartBurstyEventsAsync(时间跨度窗口)
{
while(true)
{
等待任务。延迟(窗口-TimeSpan.FromSeconds(1))。配置等待(false);
//两秒钟的突发事件
var start=DateTime.Now;
var限制=时间跨度(从秒开始)(2);
while(DateTime.Now-开始<限制)
{
等待任务。延迟(100)。配置等待(false);
WriteLine($“引发事件{eventCounter}”);
RaiseEvent($“{++eventCounter}”);
}
}
}
公共类FakeEventArgs:EventArgs
{
公共只读字符串消息;
创建的公共只读日期时间;
受保护的FakeEventArgs(){}
公共FakeEventArgs(字符串消息):
base()
{
Created=DateTime.Now;
消息=消息;
}
}
} 
混音怎么样