事件处理程序性能

时间:2011-05-10 14:05:27

标签: c# winforms optimization event-handling performance

我遇到了性能问题。我创建了100个新按钮,我想分配一个Click事件处理程序。我执行此代码大约100次:

Buttons[i].Button.Click += new System.EventHandler(Button_Click);

完成需要大约2秒。我在同一个函数中有很多其他事件赋值,但它们只需要几毫秒来执行。所以我在

中转换了我的代码
Buttons[i].Button.MouseUp += new System.Windows.Forms.MouseEventHandler(Button_Click);

现在代码很快(几毫秒,就像其他代码一样)。显然,我修改了函数“Button_click”的参数以适应新的事件要求,但没有进行其他更改。

我想知道为什么会发生这种情况。 EventHandler会变慢吗?或者我做错了什么?还是有最好的做法?

我正在使用VC2010和C#,在Windows窗体应用程序中使用.NET 4。

修改

现在我已经“缩小”了我的代码并将其放在那里:

            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();
            Button b;
            for(n=0;n<100;n++)
            {
                b = new Button();
                b.Location = new System.Drawing.Point(100, 0);
                b.Name = "btnGrid";
                b.Size = new System.Drawing.Size(50, 50);
                b.Text = b.Name;
                b.UseVisualStyleBackColor = true;
                b.Visible = false;
                b.Text = "..";
                b.Click += new EventHandler(this.Button_Click);
                //b.MouseUp += new System.Windows.Forms.MouseEventHandler(this.Button_ClickUP);
            }
            stopWatch.Stop();

            TimeSpan ts = stopWatch.Elapsed;
            string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);
            Log(elapsedTime, Color.Purple);

Button_Click和Button_Click是:

    private void Button_Click(object sender, EventArgs e)
    {            
    }

    private void Button_ClickUP(object sender, MouseEventArgs e)
    {
    }

我将此代码放在一个按钮中,“Log”功能将结果显示在备忘录中。当我启用“Click”时,结果为01.05秒,但是当我启用“MouseUp”时,结果为00.00。

差异 - &gt;一秒钟!

为什么!?

==编辑==

我使用的是.NET Framework 4. VS2010。赢得XP。我发现了这个:如果我使用.NET 3.5或更低的速度变化:0.5秒。一半。 如果我在调试或发布模式下编译它不会改变。

如果我在没有调试器的情况下使用可执行文件的速度非常快。

所以我改变了我的问题:.NET 4比.NET 3慢吗?与独立版本相比,为什么发布模式的工作方式不同?

非常感谢。

4 个答案:

答案 0 :(得分:5)

代码“.Click + = ...”转换为“.add_Click(...)”。 “add_Click”方法可以进行一些逻辑检查。

你可以通过不重新授权来加快速度:

EventHandler clickHandler = this.Button_Click;
foreach(Button btn in GetButtons()) {
   btn.Click += clicHandler;
}

修改

您确定,瓶颈是连接处理程序吗? 我尝试了for循环(100循环)并将eventhandler附加到Click事件,我得到了这个结果:

/* only creation the button and attaching the handler */
button1_Click - A: 0 ms
button1_Click - B: 0 ms
button1_Click - A: 1 ms
button1_Click - B: 0 ms
button1_Click - A: 0 ms
button1_Click - B: 0 ms

/* creation the button, attaching the handler and add to the panel */
button2_Click - A: 223 ms
button2_Click - B: 202 ms
button2_Click - A: 208 ms
button2_Click - B: 201 ms
button2_Click - A: 204 ms
button2_Click - B: 230 ms

源代码:

    void button_Click(object sender, EventArgs e) {
        // do nothing
    }

    private void button1_Click(object sender, EventArgs e) {
        const int MAX_BUTTONS = 100;
        var stopWatch = new System.Diagnostics.Stopwatch();
        stopWatch.Start();
        for (int i = 0; i < MAX_BUTTONS; i++) {
            var button = new Button();
            button.Click += new EventHandler(button_Click);
        }
        stopWatch.Stop();
        System.Diagnostics.Debug.WriteLine(string.Format("button1_Click - A: {0} ms", stopWatch.ElapsedMilliseconds));

        stopWatch.Reset();
        stopWatch.Start();
        EventHandler clickHandler = this.button_Click;
        for (int i = 0; i < MAX_BUTTONS; i++) {
            var button = new Button();
            button.Click += clickHandler;
        }
        stopWatch.Stop();
        System.Diagnostics.Debug.WriteLine(string.Format("button1_Click - B: {0} ms", stopWatch.ElapsedMilliseconds));
    }

    private void button2_Click(object sender, EventArgs e) {
        const int MAX_BUTTONS = 100;

        var stopWatch = new System.Diagnostics.Stopwatch();

        this.panel1.Controls.Clear();
        stopWatch.Start();
        for (int i = 0; i < MAX_BUTTONS; i++) {
            var button = new Button();
            button.Click += new EventHandler(button_Click);
            this.panel1.Controls.Add(button);
        }
        stopWatch.Stop();
        System.Diagnostics.Debug.WriteLine(string.Format("button2_Click - A: {0} ms", stopWatch.ElapsedMilliseconds));

        stopWatch.Reset();

        this.panel1.Controls.Clear();
        stopWatch.Start();
        EventHandler clickHandler = this.button_Click;
        for (int i = 0; i < MAX_BUTTONS; i++) {
            var button = new Button();
            button.Click += clickHandler;
            this.panel1.Controls.Add(button);
        }
        stopWatch.Stop();
        System.Diagnostics.Debug.WriteLine(string.Format("button2_Click - B: {0} ms", stopWatch.ElapsedMilliseconds));
    }

编辑2: 我尝试比较附加Click处理程序与附加MouseUp处理程序所花费的时间。看来,附加的MouseUp事件比Click事件更快。

我认为问题会出在其他地方。在循环期间不收集GC吗?或者你不在那里做别的什么?

结果:

button1_Click - Click_A: 6 ms
button1_Click - Click_B: 6 ms
button1_Click - MouseUp_A: 15 ms
button1_Click - MousUp_B: 7 ms

button1_Click - Click_A: 16 ms
button1_Click - Click_B: 7 ms
button1_Click - MouseUp_A: 16 ms
button1_Click - MousUp_B: 10 ms

button1_Click - Click_A: 14 ms
button1_Click - Click_B: 19 ms
button1_Click - MouseUp_A: 27 ms
button1_Click - MousUp_B: 5 ms

button1_Click - Click_A: 17 ms
button1_Click - Click_B: 17 ms
button1_Click - MouseUp_A: 24 ms
button1_Click - MousUp_B: 8 ms

button1_Click - Click_A: 6 ms
button1_Click - Click_B: 5 ms
button1_Click - MouseUp_A: 14 ms
button1_Click - MousUp_B: 7 ms

button1_Click - Click_A: 14 ms
button1_Click - Click_B: 9 ms
button1_Click - MouseUp_A: 15 ms
button1_Click - MousUp_B: 7 ms

代码:

    private void button1_Click(object sender, EventArgs e) {
        const int MAX_BUTTONS = 1000;
        var stopWatch = new System.Diagnostics.Stopwatch();

        stopWatch.Start();
        for (int i = 0; i < MAX_BUTTONS; i++) {
            var button = new Button();
            button.Click += new EventHandler(button_Click);
        }
        stopWatch.Stop();
        System.Diagnostics.Debug.WriteLine(string.Format("button1_Click - Click_A: {0} ms", stopWatch.ElapsedMilliseconds));

        stopWatch.Reset();
        stopWatch.Start();
        EventHandler clickHandler = this.button_Click;
        for (int i = 0; i < MAX_BUTTONS; i++) {
            var button = new Button();
            button.Click += clickHandler;
        }
        stopWatch.Stop();
        System.Diagnostics.Debug.WriteLine(string.Format("button1_Click - Click_B: {0} ms", stopWatch.ElapsedMilliseconds));

        stopWatch.Start();
        for (int i = 0; i < MAX_BUTTONS; i++) {
            var button = new Button();
            button.MouseUp += new MouseEventHandler(button_MouseUp);
        }
        stopWatch.Stop();
        System.Diagnostics.Debug.WriteLine(string.Format("button1_Click - MouseUp_A: {0} ms", stopWatch.ElapsedMilliseconds));

        stopWatch.Reset();
        stopWatch.Start();
        MouseEventHandler mouseUpHandler = this.button_MouseUp;
        for (int i = 0; i < MAX_BUTTONS; i++) {
            var button = new Button();
            button.MouseUp += mouseUpHandler;
        }
        stopWatch.Stop();
        System.Diagnostics.Debug.WriteLine(string.Format("button1_Click - MousUp_B: {0} ms", stopWatch.ElapsedMilliseconds));
    }

编辑: add_Click方法(= Click += ...)的正文很粗糙:

public void add_Click(EventHandler value) {
   this.Events.AddHandler(ClickEventIdentifier, value);
}

MouseUp事件看起来很相似。至少两个事件都使用Events属性来保存事件的委托列表。

但是,如果我尝试了几件事,我就无法解决你写的事件的问题:(。 你能在另一台计算机上重现相同的行为吗?

答案 1 :(得分:4)

System.EventHandler是委托类型,因此不会执行任何操作。这不能成为表现差异的根源 在内部添加新的Click处理程序和新的MouseUp事件处理程序是相同的。两者都致电Events.AddHandler 在我看来,唯一的区别可能是,Click已经附加了其他事件处理程序而MouseUp没有或反过来。
要检查我的假设是否正确,您可以复制并粘贴两个代码片段并执行两次,并测量第一次和第二次的持续时间。

如果Click的两次运行都很慢而且MouseUp的第二次运行速度很慢,则问题是,已存在Click个处理程序并且已经存在一个处理程序比不存在时慢一个。

如果Click的第一次运行速度很慢而第二次运行很快且MouseUp的两次运行都很快,则问题是,没有现有的Click处理程序并且添加已存在的处理程序更快比不存在时添加一个

我的回答是假设OP的观察结果是无副作用的。我实际上并没有测试他的结果是否可重复或合理。我的回答只是想证明Click事件或System.EventHandler确实没什么特别的。

答案 2 :(得分:0)

我试过了: -

  public partial class Form1 : Form
  {
    List<Button> buttonList = new List<Button>();
    public Form1()
    {
      for (int n = 0; n < 100; n++)
      {
        Button tempButt = new Button();
        tempButt.Top = n*5;
        tempButt.Left = n * 5;
        this.Controls.Add(tempButt);
        buttonList.Add(tempButt);
      }
      var stopwatch = new Stopwatch();
      stopwatch.Start();
      foreach (Button butt in buttonList)
      {
        butt.Click += new System.EventHandler(button1_Click);
      }
      stopwatch.Stop();
      Console.WriteLine(stopwatch.ElapsedMilliseconds);
    }

    private void button1_Click(object sender, EventArgs e)
    {
      Console.WriteLine("Cheese");
    }
  }

显示所有按钮并且事件似乎正确触发,事件处理程序分配太快而无法合理地测量。我的机器上不到一秒钟。

也许问题出在其他地方?

答案 3 :(得分:0)

尝试关闭IntelliTrace。有完全相同的症状。不在Visual Studio下运行时速度极快,但每个btn总是增加30ms。如果在Visual Studio下运行,则点击+ = i。

所以很可能IntelliTrace以某种方式挂钩到Click事件以进行详细的调试。