我正在尝试创建一个c#WinForms应用程序,它可以在RichTextBox中搜索并突出显示文本。我创建了两种搜索方法:一种在GUI线程中运行,另一种在BackGroundWorker中运行。两种方法的逻辑基本相同。但是,BGW中的代码运行速度要慢得多。为什么在我的BackGroundWorker线程中相同的代码比在我的GUI线程中慢得多?
请参阅下面的结果:
0.25 MB文本文件搜索一些常见的关键字:GUI:2.9s - BGW:7.0s
1MB文本文件搜索一些常见的关键字:GUI:14.1s - BGW:71.4小号
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);
}
}
注:
- 我知道RTB类都有自己的查找方法,但发现这是比我自己的要慢得多方法。
- 我已经阅读了许多有关BGW性能的主题,并且大多数网站似乎都使用Invoke方法作为原因,但我没有使用任何方法。
- 我明白多线程的使用会让它运行速度变慢,但并没有期待这么大的差别。
- 问题不在
ReportProgress
我已经评论了这条线。我这样做的原因,而不是作为一个百分比是计算,以确定百分比有很大的不同。它实际上更快这种方式 - 这link由另一位用户提供描述我如何在非GUI线程中使用我的RTB。它似乎暗示它不应该是一个问题,但会招致更多的开销,因为它会导致创建消息队列。我不确定这是否会影响我的foreach循环中的代码的性能。对此事的任何意见将不胜感激。
也许后台线程的优先级设置过低屏幕的可见区域? “基本相同的代码”也不是相同的代码。 – GCaiazzo
@GCaiazzo感谢您的评论。我尝试设置优先级,如下所示:'System.Diagnostics.Process.GetCurrentProcess()。PriorityClass = System.Diagnostics.ProcessPriorityClass.High;'但它似乎没有什么区别。 (我知道这是一个坏主意,因为线程是汇集的,我只是做了一个测试)。当我说基本相同时,我指的是foreach循环中的逻辑。这是一样的。我认为^^ – mfa
我正在看的代码实际上是一件坏事(tm)。第一个问题是你正在后台线程上创建一个'Control'('RichTextBox')。作为一个经验法则,只需在主UI线程上创建一个控件。当你在后台线程上创建一个'Control'时,你在背景中做了一堆废话,不应该在后台线程上完成。相反,将字符串传递给后台线程,让后台线程返回突出显示的索引,以便前台线程可以突出显示后台线程找到的文本块。 –