C# UI冻结和计算速度非常慢

C# UI冻结和计算速度非常慢,c#,multithreading,user-interface,freeze,C#,Multithreading,User Interface,Freeze,我正在编写一个程序,它应该替换或删除logfile.txt中的一些条目。 代码运行良好(至少对于小日志文件)。如果我使用一个大文件(比如27MB),它会变得非常慢,UI会冻结。我不能点击任何东西 单击按钮后,我执行此方法: private string delete_Lines(string[] lines, string searchString) { for (int i = 0; i < lines.Length; i++) {

我正在编写一个程序,它应该替换或删除logfile.txt中的一些条目。 代码运行良好(至少对于小日志文件)。如果我使用一个大文件(比如27MB),它会变得非常慢,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;
    }
private string delete_行(string[]行,string searchString)
{
对于(int i=0;i
Lines是我试图清理的日志文件的数组。每个条目都是一行。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;
        }

    }
private bool是下一行块(字符串[]行,int curIndex)
{
if(curIndex<线长度-1)
{
if(第[curIndex+1]行)。以(“”)开头
{
返回true;
}
其他的
{
返回false;
}
}
其他的
{
返回false;
}
}

有没有人知道是什么原因导致了程序冻结和减速?我知道,我可以通过并行化来加快代码的速度,但我无法想象,在没有并行性的情况下检查一个27 MB的txt文件需要这么长时间。

这里有几个问题:

  • 您正在读取缓冲区中的整个文件(字符串数组),我猜您正在调用file.ReadAllLines()。读取缓冲区中的大文件会减慢速度,在极端情况下会耗尽内存

  • 您正在对rich textbox文本属性使用+=操作。这是一个耗时的操作,因为每次以这种方式更新文本属性时,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()
    用法的后续问题,通常是这样做的:


    希望这有帮助

    感谢大家的帮助,特别是loopedcode,这是一个工作版本(使用了loopedcode的代码并进行了一些编辑):


    BackgroundWorker
    确实使用线程,而且它是专门为允许对UI进行进度更新而设计的。如果你很难弄清楚这一点,可以问一个具体的问题。至于性能,使用其他线程模型不会改变性能。如果需要很长时间来处理27MB的文件,则可能是该文件位于有史以来最慢的磁盘上,或者您的处理速度太慢。在任何情况下,您都需要将此示例缩小到更简单的范围,并一次只关注一个问题。请参阅并认真考虑将代码移到单独的类,并使该类的调用需要适当的参数,而不是您现在的操作方式。现在实例这个类,并通过
    Task.Factory.Start()
    调用它,然后告诉我它是否仍在减慢你的应用程序。记住添加一个
    。如果需要设置/取消设置视觉提示(例如进度条等),请继续使用
    。@PeterDuniho我编辑我的问题和代码块,以便更好地阅读。谢谢你的建议。在code4life:为了确保我正确理解您:您希望我将整个代码(除了ui中的部分)移动到一个新类中,并在那里进行计算吗?老实说,我不知道你所说的Task.Factory.Start()是什么意思,以及到底是什么意思。ContinueWith?根据你的编辑,你的具体问题是什么?问“有什么想法或建议吗?”至少可以说有点宽泛和模糊。请返回并重新阅读我在第一篇评论中提供的链接。我已经在介绍中写下了我的问题,但是我另一次编辑了这个帖子,也许现在已经足够清楚了。我也读了你贴的链接。工作很有魅力。泰利。需要做一些调整,但我想主要工作已经完成了。当我完成后,我会发布工作代码,以防有人对这个问题的工作结果感兴趣。已经修好了,但我会记住的。
    
    // 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();
       }));
    }
    
            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!);
        }