c# - 如何减少连续触发事件的事件处理频率

标签 c# async-await task-parallel-library

我正在学习 c# 中的任务和 async/await。所以请考虑我的问题是否愚蠢。

类中有一个事件DummyEvent。事件处理程序 DummyEventHandler 订阅了此 event,它处理大量 CPU 绑定(bind)任务,实际上不需要如此频繁地使用。

因此,如果 DummyEvent 连续触发,我希望 DummyEventHandler 以降低的频率响应,或者在连续性结束时响应。

所以,我的想法是将大型任务提取到一个单独的任务中,并使其在继续之前延迟 500 毫秒。延迟结束后,它将检查是否再次安排了相同的任务(连续事件触发),如果为真,则避免大量计算。

这是我对这个想法的幼稚实现:

int ReducedCall = 0;
int TotalCallActual = 0;

protected void DummyEventHandler(object sender, bool arg)
{
    TotalCallActual++;
    LargeCPUBoundTask(); // there is a green underline here, but I think it's ok, or.. is it?
}

async Task LargeCPUBoundTask()
{
    ReducedCall = TotalCallActual;

    await Task.Delay(500);
    // if this task is called again in this time, TotalCallActual will increase

    if (ReducedCall == TotalCallActual)
    {
        // do all the large tasks
        ……

        ReducedCall = 0;
        TotalCallActual = 0;
    }
}

但问题是,我没有得到我想要的。 Task.Delay(500) 行实际上并没有等待,或者,如果确实等待,则有问题,因为我遇到了惊人的问题。

任何更好的想法,或任何改进/更正?

询问任何其他信息。

谢谢

最佳答案

您可以利用 Reactive Extensions这样做:

void Main()
{
    var generator = new EventGenerator();
    var observable = Observable.FromEventPattern<EventHandler<bool>, bool>(
                h => generator.MyEvent += h,
                h => generator.MyEvent -= h);

    observable
        .Throttle(TimeSpan.FromSeconds(1))
        .Subscribe(s =>
        {
            Console.WriteLine("doing something");
        });

    // simulate rapid firing event
    for(int i = 0; i <= 100; i++)
        generator.RaiseEvent(); 

    // when no longer interested, dispose the subscription  
    subscription.Dispose(); 
}

public class EventGenerator
{
    public event EventHandler<bool> MyEvent;

    public void RaiseEvent()
    {
        if (MyEvent != null)
        {
            MyEvent(this, false);
        }
    }
}

Throttle上面编码的运算符将允许值(事件)每秒变为真。

所以在上面的代码示例中,文本doing something 只会打印一次(一秒钟后),即使事件被触发多次也是如此。

编辑
顺便说一句,绿线的原因是你的任务没有等待。要修复它,请将代码更改为:

protected async void DummyEventHandler(object sender, bool arg)
{
    TotalCallActual++;
    await LargeCPUBoundTask(); // there is no more green underline here
}

不幸的是,这仍然不能解决您的问题,因为无法等待事件,因此如果在 LargeCPUBoundTask 仍在运行时再次引发事件,将再次调用 LargeCPUBoundTask所以如果你明白我的意思,工作就是重叠的。换句话说,这就是您的代码不起作用的原因。

关于c# - 如何减少连续触发事件的事件处理频率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54186923/

相关文章:

c# - 如何将任务结果传递给不使用延续的其他任务

c# - 详细信息 GridView 意外刷新整个页面

c# - 如果所有消息都在本地发送,使用服务总线是否会过头?

javascript - 带有 promise/async-await 的回调上下文

c# - Windows 8 Metro App 仅在读取文件时在调试器中启动

c# - Dispatcher.BeginInvoke 和 Task.Factory.StartNew 有什么区别

c# - 基于 Session 值的 MVC OutputCache

C# - 动态添加对象(添加动态属性名称)

c# - 带有 C# 驱动程序 2.0 : How to get the result from InsertOneAsync 的 MongoDB(服务器 v 2.6.7)

f# - F# 并行序列中的奇怪行为