Производительность обработчика событий

У меня проблема с производительностью. Я создаю 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 с С#, используя .NET 4 в приложении Windows Form.

EDIT:

Теперь я "уменьшил" свой код, и я положил его туда:

            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)
    {
    }

Я помещаю этот код в кнопку, и функция "Журнал" отображает результат в памятке. Когда я включаю "Click", результат составляет 01.05 сек, но когда я включаю "MouseUp", результат равен 00.00.

Разница → ОДИН ВТОРОЙ!

почему!?

== EDIT ==

Я использую .NET Framework 4. VS2010. Выиграйте XP. Я нашел это: , если я использую .NET 3.5 или ниже скорость изменения: 0.5 сек. Половина. Если я компилирую в режиме отладки или выпуска, он не изменяется.

Если я использую исполняемый файл без отладчика, он быстро вспыхивает.

Итак, я меняю свой вопрос:.NET 4 медленнее, чем .NET 3? Почему режим Release работает по-другому по сравнению с автономной версией?

Большое спасибо.

Ответы

Ответ 1

Код ".Click + =..." преобразуется в ".add_Click (...)". Метод "add_Click" может иметь некоторые логические проверки.

Вы можете немного увеличить скорость без отдыха делегата:

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

EDIT:

Вы уверены, узким местом является привязка обработчиков? Я попробовал цикл for (100 циклов) с привязкой обработчика событий к событию 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 handler против прикрепления обработчика 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 для хранения списков делегатов для событий.

Но если я пробовал несколько вещей, я не могу получить проблемы с событиями, как вы писали:(. Можете ли вы воспроизвести такое же поведение на других компьютерах?

Ответ 2

System.EventHandler - это тип делегата и, следовательно, ничего не делает. Это не может быть причиной разницы в производительности.
Добавление нового обработчика Click и нового обработчика событий MouseUp является внутренним. Оба вызова Events.AddHandler.
На мой взгляд, единственное отличие может заключаться в том, что либо Click уже имеет другие обработчики событий, а MouseUp не имеет другого способа. Чтобы проверить, правильно ли мое предположение, вы можете скопировать и вставить два фрагмента кода и выполнить их дважды дважды и измерить продолжительность для первого и второго времени.

Если оба запуска для Click медленны, а второй запуск для MouseUp медленный, проблема в том, что уже существуют существующие обработчики Click и добавление обработчика, когда уже существует, медленнее, чем добавление одного, когда нет.

Если первый запуск для Click медленный, а второй быстрый и оба для MouseUp выполняются быстро, проблема в том, что нет существующих обработчиков Click и добавления обработчика, когда уже существует один быстрее, чем добавлять один, когда не существует.

В моем ответе предполагается, что наблюдения ОП свободны от побочных эффектов. Я действительно не проверял, являются ли его результаты воспроизводимыми или правдоподобными. Мой ответ просто хочет показать, что нет ничего особенного для события Click или System.EventHandler.

Ответ 3

Я пробовал это: -

  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");
    }
  }

Появляются все кнопки, и события, похоже, срабатывают правильно, назначение обработчика событий слишком быстро для разумного измерения. Гораздо меньше секунды на моей машине.

Возможно, проблема кроется в другом месте?

Ответ 4

Попробуйте отключить IntelliTrace. Имели те же самые симптомы. Быстро вспыхивает, когда не запускается в Visual Studio, но всегда добавляется 30 мс на бит. Нажмите + = i, если вы работаете в Visual Studio.

Итак, вероятно, IntelliTrace, который каким-то образом перехватывает события Click для детальной отладки.