【问题标题】:UI Freezing and Computation Really SlowUI冻结和计算真的很慢
【发布时间】:2015-02-01 20:36:43
【问题描述】:

我正在编写一个程序,它应该替换或删除 logfile.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;
    }

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;
        }

    }

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

【问题讨论】:

  • BackgroundWorker 确实使用线程,它专门设计用于允许对 UI 进行进度更新。如果您无法弄清楚这一点,请专门提出一个问题。至于性能,使用其他一些线程模型不会改变性能。如果处理 27MB 需要很长时间,那么要么该文件位于有史以来最慢的磁盘上,要么您的处理速度太慢。无论如何,您需要将此示例缩小到更简单的范围,并一次只关注一个问题。见stackoverflow.com/help/mcvestackoverflow.com/help/how-to-ask
  • 认真考虑将代码移动到一个单独的类并调用该类需要适当的参数,而不是你现在的做法。现在实例化这个类并通过Task.Factory.Start() 调用它,并告诉我它是否仍在拖慢您的应用程序。如果您需要设置/取消设置视觉提示(例如进度条等),请记得添加.ContinueWith
  • @PeterDuniho 我编辑了我的问题和代码块,以便更好地阅读。 ty 的建议。在code4life:只是为了确定,我理解你正确:你想让我将我的整个代码(期望来自ui的部分)移动到一个新类并在那里进行我的计算?老实说,我不知道您所说的 Task.Factory.Start() 是什么意思,.ContinueWith 到底是什么意思?
  • 鉴于您的编辑,您的具体问题是什么?询问“有什么想法或建议吗?”至少可以说有点宽泛和模糊。还请返回并重新阅读我在第一条评论中提供的链接。
  • 我已经在简介中写下了我的问题,但我再次编辑了这个帖子,也许它现在已经足够清楚了。我还阅读了您发布的链接。

标签: c# multithreading user-interface freeze


【解决方案1】:

这里有几个问题:

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

  2. 您正在为富文本框 Text 属性使用 += 操作。这是一个耗时的操作,因为每次您以这种方式更新文本属性时,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");
}

【讨论】:

  • 工作就像一个魅力。太棒了。需要进行一些调整,但我想主要工作已经完成。完成后我会发布工作代码,以防有人对此问题的工作结果感兴趣。
【解决方案2】:

作为您对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();
   }));
}

希望这会有所帮助!

【讨论】:

  • 解释。已经修好了,但记不住了。
【解决方案3】:

感谢大家的帮助,尤其是 loopedcode,这是工作版本(获取 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!);
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-09-16
    • 2021-03-27
    • 2016-09-09
    • 1970-01-01
    • 1970-01-01
    • 2023-02-08
    • 2017-11-10
    • 2010-12-28
    相关资源
    最近更新 更多