UI 冻结和计算非常慢
本文关键字:非常 计算 冻结 UI | 更新日期: 2023-09-27 18:32:58
我正在编写一个程序,它应该替换或删除日志文件中的一些条目.txt。代码工作正常(至少对于小型日志文件)。如果我使用一个大文件(如 27 MB),它会变得非常慢并且 UI 冻结。我无法点击任何东西。
在按钮单击时,我执行此方法:
private string delete_Lines(string[] lines, string searchString)
{
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Contains(searchString))
{
rtbLog.Text += "Deleting(row " + (i + 1) + "):'n" + lines[i] + "'n";
progressBar1.Value += 1;
if (cbDB == true)
{
while (is_next_line_block(lines, i) == true)
{
i++;
rtbLog.Text += lines[i] + "'n";
progressBar1.Value += 1;
}
}
}
else
{
res += lines[i]+"'n";
progressBar1.Value += 1;
}
}
tssLbl.Text = "Done!";
rtbLog.Text += "...Deleting finished'n";
return res;
}
行是我正在尝试清理的日志文件的数组。 每个条目都是一行。 tssLbl是一个通知标签,rtbLog是一个richTextBox,我正在跟踪我正在删除哪一行。
is_next_line_block只是另一种方法,即检查下一行是否是我要删除的块的一部分。此方法的参数是整个线数组和线位置。
private bool is_next_line_block(string[] lines, int curIndex)
{
if (curIndex < lines.Length-1)
{
if (lines[curIndex + 1].StartsWith(" "))
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
有没有人知道,是什么原因导致冻结并减慢程序速度?我知道,我可以通过并行化来加快我的代码速度,但我无法想象,在没有并行性的情况下检查一个 27 MB 的 txt 文件需要这么长时间。
您在这里有几个问题:
- 您正在
缓冲区(字符串数组)中读取整个文件,我猜您正在调用File.ReadAllLines()。在缓冲区中读取大文件会减慢您的速度,并且在极端情况下会耗尽内存。
您正在对格式文本框文本属性使用 += 操作。这是耗时的操作,因为每次以这种方式更新文本属性时,UI 都必须呈现整个富文本框。更好的选择是使用字符串生成器加载这些文本,并定期更新富文本框。
要解决此问题,您需要将文件作为流读取。可以根据读取的字节而不是行位置来监视进度。您可以在计时器上异步运行读取操作并监视进度,如以下示例所示。
private void RunFileOperation(string inputFile, string search)
{
Timer t = new Timer();
int progress = 0;
StringBuilder sb = new StringBuilder();
// Filesize serves as max value to check progress
progressBar1.Maximum = (int)(new FileInfo(inputFile).Length);
t.Tick += (s, e) =>
{
rtbLog.Text = sb.ToString();
progressBar1.Value = progress;
if (progress == progressBar1.Maximum)
{
t.Enabled = false;
tssLbl.Text = "done";
}
};
//update every 0.5 second
t.Interval = 500;
t.Enabled = true;
// Start async file read operation
System.Threading.Tasks.Task.Factory.StartNew(() => delete_Lines(inputFile, search, ref progress, ref sb));
}
private void delete_Lines(string fileName, string searchString, ref int progress, ref StringBuilder sb)
{
using (var file = File.OpenText(fileName))
{
int i = 0;
while (!file.EndOfStream)
{
var line = file.ReadLine();
progress = (int)file.BaseStream.Position;
if (line.Contains(searchString))
{
sb.AppendFormat("Deleting(row {0}):'n{1}", (i + 1), line);
// Change this algorithm for nextline check
// Do this when it is next line, i.e. in this line.
// "If" check above can check if (line.startswith(" "))...
// instead of having to do it nextline next here.
/*if (cbDB == true)
{
while (is_next_line_block(lines, i) == true)
{
i++;
rtbLog.Text += lines[i] + "'n";
progressBar1.Value += 1;
}
}*/
}
}
}
sb.AppendLine("...Deleting finished'n");
}
作为您关于Task.Factory.Start()
用法问题的后续,它是这样完成的(通常):
// you might need to wrap this in a Dispatcher.BeginInvoke (see below)
// if you are not calling from the main UI thread
CallSomeMethodToSetVisualCuesIfYouHaveOne();
Task.Factory.StartNew(() =>
{
// code in this block will run in a background thread...
}
.ContinueWith(task =>
{
// if you called the task from the UI thread, you're probably
// ok if you decide not to wrap the optional method call below
// in a dispatcher begininvoke...
Application.Current.Dispatcher.BeginInvoke(new Action(()=>
{
CallSomeMethodToUnsetYourVisualCuesIfYouHaveAnyLOL();
}));
}
希望这有帮助!
感谢大家的帮助,尤其是循环代码,这是工作版本(获取了loopedcode的代码并进行了一些编辑):
private void RunFileOperation(string inputFile, string search)
{
Timer t = new Timer();
StringBuilder sb = new StringBuilder();
{
rtbLog.Text = "Start Deleting...'n";
}
// Filesize serves as max value to check progress
progressBar1.Maximum = (int)(new FileInfo(inputFile).Length);
t.Tick += (s, e) =>
{
rtbLog.Text += sb.ToString();
progressBar1.Value = progress;
if (progress == progressBar1.Maximum)
{
t.Enabled = false;
tssLbl.Text = "done";
}
};
//update every 0.5 second
t.Interval = 500;
t.Enabled = true;
// Start async file read operation
if (rbtnDelete.Checked)
{
if (cbDelete.Checked)
{
System.Threading.Tasks.Task.Factory.StartNew(() => delete_Lines(inputFile, search, ref progress, ref sb, ref res1));
}
}
else
{
//..do something
}
private void delete_Lines(string fileName, string searchString, ref int progress, ref StringBuilder sb, ref StringBuilder res1)
{
bool checkNextLine=false;
using (var file = File.OpenText(fileName))
{
int i = 0;
while (!file.EndOfStream)
{
i++;
var line = file.ReadLine();
progress = (int)file.BaseStream.Position;
if (line.Contains(searchString))
{
sb.AppendFormat("Deleting(row {0}):'n{1}'n", (i), line);
checkNextLine = true;
}
else
{
if (cbDB && checkNextLine && line.StartsWith(" "))
{
sb.AppendFormat("{0}'n", line);
}
else
{
checkNextLine = false;
res1.AppendLine(line);
}
}
}
}
sb.AppendLine("'n...Deleting finished!);
}