Почему один и тот же код в моем потоке BackGroundWorker работает намного медленнее, чем в моем потоке графического интерфейса?

Я пытаюсь создать приложение WinForms на C #, которое ищет и выделяет текст в RichTextBox. Я создал два метода поиска: один работает в потоке графического интерфейса пользователя, а другой - в BackGroundWorker. Логика в обоих методах по сути идентична. Однако код в BGW работает значительно медленнее.

См. Результаты ниже:

0,25 МБ Текстовый файл с поиском по общему ключевому слову: GUI: 2,9 с - BGW: 7,0 с
1 МБ Текстовый файл для поиска по общему ключевому слову: GUI: 14.1s - BGW: 71.4s
5 МБ Текстовый файл для поиска по общему ключевому слову: GUI: 172s - BGW: 1545s

Мне кажется странным, что соотношение между временем, затрачиваемым на эти два метода, не зависит от размера поиска.

Приложение будет использоваться для поиска файлов размером до 10 МБ, поэтому важно, чтобы это выполнялось быстро. Я хотел использовать фонового рабочего, чтобы пользователь мог видеть прогресс и продолжать читать файл, пока выполняется поиск.

См. Код для двух методов ниже:

    // background search thread
    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        // Get the BackgroundWorker that raised this event.
        BackgroundWorker worker = sender as BackgroundWorker;

        RichTextBox rtb = new RichTextBox();
        RichTextBox results = new RichTextBox();
        rtb.Rtf = e.Argument as string;  //recive text to be searched

        int hits = 0; // track number of hits
        int pos = 0;  // track position in rtb
        int i = 0;    // trach current line number for progress report

        string lowerT = searchTerm.ToLowerInvariant();
        string lowerl = "";
        int n = 0;
        int len = searchTerm.Length;

        foreach (string l in rtb.Lines)
        {
            lowerl = l.ToLowerInvariant();
            n = lowerl.IndexOf(lowerT);
            if (n > -1)
            {
                while (n > -1)   //if found sterm highlight instances
                {
                    hits++;     //incriment hits

                    //hilight term
                    rtb.SelectionStart = pos + n;
                    rtb.SelectionLength = len;
                    rtb.SelectionBackColor = Color.Yellow;
                    rtb.SelectionColor = Color.Black;

                    //find next
                    n = lowerl.IndexOf(lowerT, n + len);
                }
                searchRes.Add(pos); // add positon of hit to results list

                //add rtb formatted text to results rtb
                rtb.SelectionStart = pos;
                rtb.SelectionLength = l.Length;
                results.SelectedRtf = rtb.SelectedRtf;
                results.AppendText(Environment.NewLine);

            }
            pos += l.Length + 1; //incriment position

            //worker.ReportProgress(++i);
        }
        string[] res = {rtb.Rtf,results.Rtf,hits.ToString()};
        e.Result = res;
    }

    // old non threaded search method
    public void OldSearch(string sTerm)
    {
        int hits = 0; // track number of hits
        int pos = 0;  // track position in rtb
        int oldPos = richTextBox1.SelectionStart; //save current positin in rtb
        int oldLen = richTextBox1.SelectionLength;

        string lowerT = sTerm.ToLowerInvariant();

        sTime = 0;
        System.Threading.Timer tmr = new System.Threading.Timer(new TimerCallback(TimerTask), null, 0, 100);

        if (sTerm.Length > 0)
        {
            //clear old search
            ReloadFile();
            richTextBox4.Clear();
            searchRes = new List();

            //open results pane
            label1.Text = "Searching for \"" + sTerm + "\"...";
            splitContainer1.Panel2Collapsed = false;

            frmFind.Focus();
            frmFind.ShowProgress(true);

            foreach (string l in richTextBox1.Lines)
            {
                string lowerl = l.ToLowerInvariant();
                int n = lowerl.IndexOf(lowerT);
                if (n > -1)
                {
                    while (n > -1)   //if found sterm highlight instances
                    {
                        hits++;     //incriment hits
                        //hilight term
                        richTextBox1.SelectionStart = pos + n;
                        richTextBox1.SelectionLength = sTerm.Length;
                        richTextBox1.SelectionBackColor = Color.Yellow;
                        richTextBox1.SelectionColor = Color.Black;
                        //find next
                        n = lowerl.IndexOf(lowerT, n + sTerm.Length);
                    }
                    searchRes.Add(pos);
                    richTextBox1.SelectionStart = pos;
                    richTextBox1.SelectionLength = l.Length;
                    richTextBox4.SelectedRtf = richTextBox1.SelectedRtf;
                    richTextBox4.AppendText(Environment.NewLine);
                }
                pos += l.Length + 1; //incriment position
            }

            tmr.Dispose();

            float time = (float)sTime / 10;

            label1.Text = "Search for \"" + sTerm + "\": Found " + hits + " instances in " + time + " seconds.";
            richTextBox4.SelectionStart = 0;
            richTextBox1.SelectionStart = oldPos;
            richTextBox1.SelectionLength = oldLen;
            richTextBox1.Focus();
            frmFind.ShowProgress(false);
        }
    }

ПРИМЕЧАНИЯ:

  • Я знаю, что класс RTB имеет свой собственный метод поиска, но обнаружил, что он значительно медленнее, чем мой собственный метод.
  • Я прочитал несколько тем, касающихся производительности BGW, и большинство из них, кажется, ссылаются на использование методов Invoke как на причину, но я не использую ни одного.
  • Я понимаю, что использование нескольких потоков замедлит работу, но я не ожидал такой большой разницы.
  • Проблема не в ReportProgress Я закомментировал эту строку. Причина, по которой я делаю это таким образом, а не в процентах, заключается в том, что расчет процента имел большое значение. На самом деле это быстрее
  • Эта ссылка , предоставленная другим пользователем, описывает, как я использую свой RTB в потоке без графического интерфейса. Кажется, это предполагает, что это не должно быть проблемой, но повлечет за собой дополнительные накладные расходы, поскольку это приведет к созданию очереди сообщений. Я не уверен, повлияет ли это на производительность кода в моем цикле foreach. Будем очень признательны за любые комментарии по этому поводу.

6
задан mfa 11 December 2011 в 01:04
поделиться