带有ProgressBar的表单在读取文件时被冻结(C#WinForms)

时间:2014-02-17 03:01:15

标签: c# .net winforms backgroundworker

在我的C# WinForms 应用中,我有一个MainForm,我在其中使用BackgroundWorker来读取大型文本文件。

我还使用第二个表单来显示一个Marquee ProgressBar,告知用户他们必须等到文件被完全读取。

我遇到的问题是Form带有进度条(SimpleProgressBar)被冻结,直到读取文件为止。 BW应该在一个单独的线程上,以免发生这种情况。

我留下了读取文件的代码,因为它可能与我的问题有关。但是,所有代码实际上只是用于显示ProgressBar的Form被冻结。

SimpleProgressBar.cs(表格)

//Simple Progress Bar set to Marquee
public partial class SimpleProgressBar : Form
{
    public SimpleProgressBar()
    {
        InitializeComponent();


        //ProgressBar is setup in the designer.
        /*
          System.Windows.Forms.ProgressBar progressBar1;
          this.progressBar1.Location = new System.Drawing.Point(16, 65);
          this.progressBar1.Name = "progressBar1";
          this.progressBar1.Size = new System.Drawing.Size(350, 23);
          this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
          this.progressBar1.TabIndex = 1;
        */

    }
}

MainForm.cs(表格)

    //Class variable
    private SimpleProgressBar wait = new SimpleProgressBar();



    private void generatePreview()
    {
            //Setup BW Thread
            BackgroundWorker worker = new BackgroundWorker();
            worker.WorkerReportsProgress = true;
            worker.WorkerSupportsCancellation = true;
            worker.DoWork += worker_DoWork;
            worker.ProgressChanged += worker_ProgressChanged;
            worker.RunWorkerCompleted += worker_RunWorkerCompleted;

            //Start procesing file
            worker.RunWorkerAsync();


            //Show the dialog
            wait.ShowDialog(); //perhaps .Show() would be better ?


    }

    //Once completed put the text into the textbox
    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        textBox1.Text  = ((StringBuilder)e.Result).ToString();

    }

    //Report progress here
    void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {


    }

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker bw = (BackgroundWorker)sender;

        int bufferSize = 1024;

        var sb = new StringBuilder();
        var buffer = new Char[bufferSize];
        var length = 0L;
        var totalRead = 0L;
        var count = bufferSize; 

        using (var sr = new StreamReader("c:\200mb_text_file.txt"))
        {
            if (bw.CancellationPending)
            {

            }
            else
            {
                length = sr.BaseStream.Length;
                while (count > 0)
                {
                    count = sr.Read(buffer, 0, bufferSize);
                    sb.Append(buffer, 0, count);

                    totalRead += count;
                }
            }
        }

        e.Result = sb;


    }

更新

所以基本上我想创建一个通用的第二个表格来使用一个可用于多种目的的进度指示器。

然而,看起来BW必须位于保存需要更新的UI元素的同一个线程上。

2 个答案:

答案 0 :(得分:2)

ShowDialog显示您必须明确关闭的模式对话框。当您致电ShowDialog时,程序将不再继续执行。您的后台工作人员的完成事件必须关闭对话框。

此外,您一次读取1,024个字节,然后调用progress事件。每次调用progress事件都需要编组到UI线程。读取1,024个字节大约需要零时间,这意味着正在连续调用progress事件,这反过来意味着UI线程几乎100%被进度更新占用。

如果你确实需要在加载东西时报告进度,那么使用更大的缓冲区。无论如何,使用64 Kilobyte缓冲区,您将获得更好的读取性能。那就是:

int bufferSize = 65536;

但你的文件必须很大。你应该能够读取至少50兆字节每秒,除非你有一个非常慢的磁盘或你通过慢速网络阅读。

答案 1 :(得分:0)

进度条需要在另一个Thread中更新。更新UI线程中的进度条将导致冻结问题。只需将代码更新为Backgroudnworker的DOWork方法中的progres栏而不是报告它。

 void worker_DoWork(object sender, DoWorkEventArgs e)
    {
    stuffdone()
    progressBar1.PerformStep();

    }

在使用之前,您需要设置Form.CheckForIllegalCrossThreadCalls = false;以便BW可以访问UI