Estoy tratando de crear una aplicación WinForms C# que busca y resalta texto en un RichTextBox. Creé dos métodos de búsqueda: uno que se ejecuta en el hilo de la GUI y otro que se ejecuta en BackGroundWorker. La lógica en ambos métodos es esencialmente idéntica. Sin embargo, el código en el BGW se ejecuta considerablemente más lento.¿Por qué el mismo código es mucho más lento en mi subproceso BackGroundWorker que en mi subproceso GUI?
Por favor, vea los resultados a continuación:
archivo de texto 0.25MB buscar una palabra clave común: interfaz gráfica de usuario: 2.9s - BGW: 7.0s
archivo de texto de 1 MB buscar una palabra clave común: interfaz gráfica de usuario: 14.1s - BGW: 71.4 s
archivo de texto 5MB buscar una palabra clave común: interfaz gráfica de usuario: 172s - BGW: 1545s
parece extraño que la relación entre el tiempo necesario para que los dos métodos no se marítimo con respecto al tamaño de la búsqueda.
La aplicación se usará para buscar archivos de hasta 10MB de tamaño por lo que es importante que esto sea rápido. Quería utilizar un trabajador de fondo para que el usuario pudiera ver el progreso y continuar leyendo el archivo mientras se realizaba la búsqueda.
Por favor, vea el código de los dos métodos siguientes:
// 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);
}
}
NOTAS:
- sé que la clase RTB tiene su propio método de búsqueda, pero encontrado que esto es considerablemente más lento que mi propia método.
- He leído varios temas relacionados con el rendimiento de BGW y la mayoría parecen ubicar el uso de métodos Invoke como la causa, pero no utilizo ninguno.
- Entiendo que el uso de varios hilos hará que funcione más lento, pero no esperaba tanta diferencia.
- El problema no es con
ReportProgress
que he comentado esta línea. La razón por la que lo hago de esta manera, en lugar de como porcentaje, es que el cálculo para calcular el porcentaje hizo una gran diferencia. En realidad es más rápido de esta manera - Este link proporcionado por otro usuario describe cómo estoy usando mi RTB en un hilo que no es de GUI. Parece sugerir que no debería ser un problema, pero incurrirá en más gastos generales, ya que causará la creación de una cola de mensajes. No estoy seguro de si esto afectará el rendimiento del código dentro de mi ciclo foreach. Cualquier comentario sobre el tema sería muy apreciado.
Tal vez la prioridad del subproceso de fondo es demasiado bajo? También "código esencialmente idéntico" no es código idéntico. – GCaiazzo
@GCaiazzo Gracias por el comentario. Intenté establecer la prioridad de esta manera: 'System.Diagnostics.Process.GetCurrentProcess(). PriorityClass = System.Diagnostics.ProcessPriorityClass.High;' pero no pareció marcar la diferencia. (Entiendo que esta es una mala idea, ya que el hilo se mezcla. Lo acabo de hacer como prueba). Cuando dije esencialmente idéntico me refería a la lógica en el ciclo foreach. Que es lo mismo Creo que ^^ – mfa
El código que estoy viendo es en realidad una mala cosa (tm). El primer problema es que está creando un 'Control' (' RichTextBox') en una cadena de fondo. Como regla general, SÓLO crea un control en el hilo de la interfaz de usuario principal. Cuando creas un 'Control' en un hilo de fondo, estás haciendo un _ton_ de basura en el fondo que no debería hacerse en un hilo de fondo. En su lugar, pase una cadena a su hilo de fondo y haga que su hilo de fondo vuelva a resaltar los índices para que su hilo de primer plano pueda resaltar los bloques de texto que encontró el hilo de fondo. –