Производительность обработчика событий - PullRequest
13 голосов
/ 10 мая 2011

У меня проблема с производительностью. Я создаю 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 #, использую .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)
    {
    }

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

Разница -> ОДНА ВТОРАЯ!

почему!?

== РЕДАКТИРОВАТЬ ==

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

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

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

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

Ответы [ 4 ]

5 голосов
/ 10 мая 2011

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

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

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

РЕДАКТИРОВАТЬ:

Вы уверены, что узким местом является прикрепление обработчиков?Я попробовал цикл 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 к подключению обработчика 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));
    }

РЕДАКТИРОВАТЬ: Тело *Метод 1030 * (= Click += ...) является грубым:

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

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

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

4 голосов
/ 10 мая 2011

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

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

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

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

0 голосов
/ 12 июня 2015

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

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

0 голосов
/ 10 мая 2011

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

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

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

Может, проблема в другом?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...