在我的应用程序中,我注意到我处理事件的方式导致性能问题。性能问题 - 取消订阅事件
我想知道如果这是预期的,也许我在那里做错了什么。 有没有办法解决我的问题?
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var x = new Main();
x.Init();
Console.ReadLine();
}
}
public class Main
{
private Bar _bar;
private List<Foo> _foos;
public Main()
{
_bar = new Bar();
}
public void Init()
{
var sw = new Stopwatch();
sw.Restart();
_foos = new List<Foo>();
for (int i = 0; i < 10000; i++)
{
var newFoo = new Foo();
newFoo.Bar = _bar;
_foos.Add(newFoo);
}
sw.Stop();
Console.WriteLine("Init 10.000 Foos WITH un-subscribe event: {0} ms", sw.ElapsedMilliseconds);
_foos.Clear();
sw.Restart();
_foos = new List<Foo>();
for (int i = 0; i < 10000; i++)
{
var newFoo = new Foo();
newFoo.BarWithout = _bar;
_foos.Add(newFoo);
}
sw.Stop();
Console.WriteLine("Init 10.000 Foos WITHOUT un-subscribe event: {0} ms", sw.ElapsedMilliseconds);
_foos.Clear();
}
}
public class Bar
{
public event EventHandler<string> Stuff;
protected virtual void OnStuff(string e)
{
var stuff = this.Stuff;
if (stuff != null)
stuff(this, e);
}
}
public class Foo
{
private Bar _bar;
public Bar Bar
{
get { return _bar; }
set
{
if (_bar != null)
{
_bar.Stuff -= _bar_Stuff;
}
_bar = value;
if (_bar != null)
{
_bar.Stuff -= _bar_Stuff;
_bar.Stuff += _bar_Stuff;
}
}
}
public Bar BarWithout
{
get { return _bar; }
set
{
if (_bar != null)
{
//_bar.Stuff -= _bar_Stuff;
}
_bar = value;
if (_bar != null)
{
//_bar.Stuff -= _bar_Stuff;
_bar.Stuff += _bar_Stuff;
}
}
}
private void _bar_Stuff(object sender, string e)
{
}
}
}
在此示例代码,我Foo
类有2个属性Bar
和BarWithout
。 BarWithout
属性已取消订阅评论。
在Main
类的Init
方法我建立2倍10.000Foo
对象和所述第一例程设置Bar
性的第二设置BarWithout
属性。在我的机器上,第一个程序需要约2200毫秒,第二个程序需要约5ms。
由于差距有点巨大,我想知道是否有更有效的方法来删除事件处理程序?
顺便说一句,我知道我可以改变代码,以便Main订阅Bar的事件,并且为列表中的所有Foo对象调用一个方法,但是希望有一些“更容易”,而不需要重构现在的情况。
编辑:
具有4倍的数据(如此40.000代替10.000)的第一例程已花费〜28.000毫秒相比〜20毫秒,所以第一个例程是只用慢10倍以上4倍以上的数据。第二个例程保持不变,性能提高4倍,数据速度减慢4倍。
'_bar.Stuff - = _bar_Stuff;'的问题,它是在一个MulticastDelegate为O(n)的操作。在这个测试中,它必须通过大量代表来寻找可能的匹配。在同一个_bar变量的循环中做到这一点使得它O(n^2),二次算法开始非常快地吸吮。很难给出具体的建议,代码是非常人为的,通常_bar将是一个不同的对象。 –