65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 中简单有效的弱事件调度器

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (5投票s)

2011年4月7日

CPOL

3分钟阅读

viewsIcon

34964

downloadIcon

479

C# 中另一个弱事件调度器。

引言

一段时间前,我需要注册大量的事件处理程序。那时,我意识到如果不注销不需要的事件处理程序,就会发生内存泄漏。原因是此类处理程序的实例会持续被相应的事件调度器引用,从而阻止垃圾回收器进行垃圾回收。

另一个相关的问题是不需要的处理程序会持续被执行,因为它们的实例无法被垃圾回收,从而导致性能下降。因此,如果有可能,注销事件是一件重要的事情。

但当然,不用担心注销事件会很方便。

我发现这是一个相当常见的问题,并且在网上找到了许多解决方案和一些关于 WeakReferenceWeakEventManager 类的优秀文章。这些解决方案在某种程度上对我有效,因为它们处理了内存泄漏问题。但是,对于每个解决方案,我遇到了性能或可用性问题,所以我决定编写一个简单的弱事件调度器,重点关注可用性和性能。

背景

弱引用是指在垃圾回收器需要决定是否对相应实例进行垃圾回收时,不会被垃圾回收器计算在内的引用。这意味着弱引用可能会在某个时刻突然变为 null,因为它在没有其他强引用指向该特定实例时被垃圾回收。这也意味着在使用弱引用时需要小心。

使用代码

这个 WeakEventDispatcher 在外部表现得像一个“正常”的事件调度器,也就是说,您可以轻松地添加和删除处理程序。在内部,处理程序与指向其实例的弱引用一起被分桶。调用是通过编译的 lambda 表达式(即委托)完成的。这些委托被缓存并在可能的情况下被重用。清除 (摆脱属于垃圾回收实例的处理程序) 定期进行,可以通过阈值设置进行配置。因此,不再存在内存泄漏,并且如果您必须处理大量事件处理,事件调用的性能会显着提高。

包含一个单元测试类,该类显示了如何处理 WeakEventDispatcher。此外,还包括一个用于性能测量的测试方法。它显示了应用标准 .NET 事件处理和应用弱事件调度器的性能差异。

性能测试表明,随着事件处理迭代次数的增加,您会获得更多的性能优势。如果您大幅增加迭代次数,标准 .NET 事件处理最终会因处理程序调用的数量而纠缠在一起,因为“丢失”的实例不会被垃圾回收,因此相应的处理程序会持续被调用。 WeakEventDispatcher 阻止这种情况发生。

以下示例展示了如何应用 WeakEventDispatcher

public class Entity {
    private readonly WeakEventDispatcher<EventArgs> _changeNotificationDispatcher;

    public event EventHandler<EventArgs> DataChanged {
        add { _changeNotificationDispatcher += value; }
        remove { _changeNotificationDispatcher -= value; }
    }
 
    protected virtual void OnDataChanged(EventArgs e) {
        if(_changeNotificationDispatcher!= null)
            _changeNotificationDispatcher.Invoke(this, e);
    }
}

在事件调度器内部,需要不时进行一些内务处理,即,需要删除属于垃圾回收实例的处理程序。内务处理由 Purge() 执行。清除可以手动执行,但也会定期执行,每隔一个可配置的公共调度器方法调用次数。间隔由 PurgeThreshold 配置,相应的值可以通过调度器的构造函数传递。您可以在声明时实例化调度器并传递一个清除阈值设置,例如

private readonly WeakEventDispatcher<EventArgs> 
  _changeNotificationDispatcher = new WeakEventDispatcher<EventArgs>(10);

现在您将阈值设置为 10,这意味着每十个 Invoke(...)+= 调用将执行一次清除。因此,使用此值,您可以定义 Purge() 的执行频率。如果传递 0,则每次您触及任何公共调度器方法时都会执行 Purge()。默认情况下,清除阈值设置为 5。

© . All rights reserved.