问题描述
我遇到的问题是应用程序很慢,因为有数千个处理程序附加到它的事件,而删除这些处理程序的速度非常慢。我的应用程序作为 .NET Framework 4.8 进程运行,
我编写了一个测试程序(但使用 .NET 5),其中我用自己的实现替换了事件,其中我使用 HashSet 来存储处理程序并自己实现事件的添加/删除处理程序。
我的测试应用程序运行良好,但将此解决方案应用于我的实际应用程序后,性能仍然很差。
我意识到运行时差异似乎是原因。以下示例程序运行速度很快(在 .NET 5 上大约 1 秒,但如果在 .NET Framework 4.8 应用程序中运行相同的代码,则运行几分钟):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
namespace EventPerformaneMeasurement
{
class Program
{
private static void Main()
{
var sw = new Stopwatch();
var mc = new MyClass();
var list = Enumerable.Range(1,1_000_000).Select(x => new PropertyChangedEventHandler((_,_) => { Console.WriteLine($"Handler {x} called"); })).ToList();
sw.Restart();
list.ForEach(handler=>mc.PropertyChangednew += handler);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Restart();
list.ForEach(handler=>mc.PropertyChangednew -= handler);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
}
class MyClass
{
private HashSet<PropertyChangedEventHandler> _handlers = new();
public event PropertyChangedEventHandler PropertyChangednew
{
add => _handlers.Add(value);
remove => _handlers.Remove(value);
}
}
}
我创建了两个 .NET fiddle 来重现这种行为,.NET fiddle 要求我稍微更改示例代码的语法并将迭代次数从 1000000 减少到 15000,但性能差异仍然可见:>
我无法相信 .NET 5 和 .NET Framework 4.x 在 HashSet 性能方面存在如此大的差异。
有没有人更了解这种效果或者我可能卡在哪里?有没有办法用另一种方式在 .NET 4.8 中获得相同的性能?
解决方法
在这种情况下,由于我迫不及待地将我们的产品切换到 .NET 5,因此我需要重构以不使用事件/委托,而是使用带有接口的 Oberserver 模式并添加 /删除侦听器方法。这样的观察者对象不会有这样弱的 GetHashCode 实现,所以 HashSet 将与这样的观察者对象一起执行......