Sto provando a creare un'applicazione C# WinForms che cerca ed evidenzia il testo in un RichTextBox. Ho creato due metodi di ricerca: uno che viene eseguito nel thread GUI e uno che viene eseguito in un BackGroundWorker. La logica in entrambi i metodi è essenzialmente identica. Tuttavia, il codice nel BGW funziona molto più lentamente.Perché lo stesso codice è molto più lento nel mio thread BackGroundWorker che nel mio thread GUI?
Si prega di consultare i risultati qui sotto:
File di testo 0.25MB ricerca di una parola chiave comune: GUI: 2.9s - BGW: 7.0s
File di testo 1MB ricerca di una parola chiave comune: GUI: 14.1s - BGW: 71,4 s
5 MB di file di testo alla ricerca di una parola chiave comune: GUI: 172S - BGW: 1545s
sembra strano a me che il rapporto tra il tempo impiegato per i due metodi non tratta di rivestimento in materia di perquisizione dimensioni.
L'applicazione verrà utilizzata per la ricerca di file fino a 10 MB di dimensioni, quindi è importante che sia veloce. Volevo utilizzare un lavoratore in background in modo che l'utente potesse vedere l'avanzamento e continuare a leggere il file mentre viene eseguita la ricerca.
Si prega di vedere il codice per i due metodi indicati
// 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);
}
}
NOTE:
- So che la classe RTB ha il proprio metodo find, ma trovato questo per essere notevolmente più lento della mia metodo.
- Ho letto un numero di thread relativi alle prestazioni BGW e la maggior parte sembra utilizzare come metodo i metodi Invoke ma non ne utilizzo nessuno.
- Capisco che l'uso di più thread lo farà rallentare ma non si aspettava questa differenza.
- Il problema non è con
ReportProgress
ho commentato questa linea. Il motivo per cui lo sto facendo in questo modo piuttosto che in percentuale è il calcolo per calcolare la percentuale ha fatto una grande differenza. In questo modo è più veloce in questo modo - Questo link fornito da un altro utente descrive come sto usando il mio RTB in un thread non GUI. Sembra suggerire che non dovrebbe essere un problema, ma si incorre in ulteriori spese generali poiché causerebbe la creazione di una coda di messaggi. Non sono sicuro che ciò influenzerà le prestazioni del codice all'interno del mio ciclo foreach. Qualsiasi commento sull'argomento sarebbe molto apprezzato.
Forse la priorità del thread in background è troppo basso? Anche "codice sostanzialmente identico" non è un codice identico. – GCaiazzo
@GCaiazzo Grazie per il commento. Ho provato a impostare la priorità in questo modo: 'System.Diagnostics.Process.GetCurrentProcess(). PriorityClass = System.Diagnostics.ProcessPriorityClass.High;' ma non sembrava fare la differenza. (Capisco che questa sia una cattiva idea, dato che il thread è in pool. L'ho fatto solo come test). Quando ho detto essenzialmente identico mi riferivo alla logica del ciclo foreach. Che è lo stesso Penso ^^ – mfa
Il codice che sto guardando è in realtà una cosa cattiva (tm). Il primo problema è che stai creando un 'Control' (' RichTextBox') su un thread in background. Come regola generale, creare SOLO un controllo sul thread principale dell'interfaccia utente. Quando crei un 'Control' su un thread in background, stai facendo un _ton_ di crap sullo sfondo che non dovrebbe fare __not_ su un thread in background. Invece, passa una stringa al thread in background e il tuo thread in background restituisce gli indici di evidenziazione in modo che il thread in primo piano possa evidenziare i blocchi di testo trovati nel thread in background. –