为什么我的BackGroundWorker线程中的相同代码比我的GUI线程中的代码慢得多?

mfa*_*mfa 6 .net c# performance backgroundworker winforms

我正在尝试创建一个ac#WinForms应用程序,用于搜索和突出显示RichTextBox中的文本.我创建了两种搜索方法:一种在GUI线程中运行,另一种在BackGroundWorker中运行.两种方法中的逻辑基本相同.但是,BGW中的代码运行速度要慢得多.

请看下面的结果:

搜索常用关键字的0.25MB文本文件:GUI:2.9s - BGW:7.0s
1MB搜索常用关键字的文本文件:GUI:14.1s - BGW:71.4s
5MB搜索常用关键字的文本文件:GUI:172s - BGW:1545s

我觉得奇怪的是,这两种方法所花费的时间之间的关系不是关于搜索大小的线性关系.

该应用程序将用于搜索最大10MB的文件,因此这很重要.我想使用后台工作程序,以便用户可以看到进度并在执行搜索时继续读取文件.

请参阅以下两种方法的代码:

    // 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<int>();

            //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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 我知道RTB类有自己的find方法,但发现它比我自己的方法慢得多.
  • 我已经阅读了许多关于BGW性能的线程,并且大多数人似乎将Invoke方法的使用作为原因,但我没有使用.
  • 我理解多线程的使用会使它运行得更慢,但并没有预料到这么大的差异.
  • 问题不在于ReportProgress我已经评论过这一行.我这样做而不是百分比的原因是计算得出百分比差别很大.实际上这种方式更快
  • 另一个用户提供的此链接描述了我在非GUI线程中使用RTB的方式.它似乎表明它不应该是一个问题,但会产生更多的开销,因为它将导致创建一个消息队列.我不确定这是否会影响我的foreach循环中代码的性能.对此事的任何评论都将不胜感激.

Osk*_*orn 0

通常减慢 Winform 速度的一件事是与 UI 线程同步。如果 ReportProgress 这样做(我不知道,但我猜它必须这样做)并且你调用它太频繁(比如每秒 100-1000 次或更多),它会由于各种阻塞问题而减慢一切直至停止那将会发生。

尝试删除 UI 和后台线程之间的任何交互,如果有帮助,请恢复交互,但让它发生的频率要低得多,例如每秒 1-100 次。

另外,我不确定,但如果您传递对控件对象的引用,它可能仍然由 UI 线程拥有,并且来自另一个线程的每次交互也可能导致同步问题(以及与实际表单控件的交互)会抛出异常)。