【问题标题】:backgroundworker freeze WPF后台工作人员冻结 WPF
【发布时间】:2013-12-13 07:32:33
【问题描述】:

关注BackgroundWorker 有什么问题? 如果我尝试设置一些断点..bw_ProgressChanged 的值会更新,但如果我正在运行所有,我的 WPF 是“冻结”

    public MainWindow()
    {
        InitializeComponent();
        bw.WorkerReportsProgress = true;
        bw.WorkerSupportsCancellation = true;
    }
    private readonly BackgroundWorker bw = new BackgroundWorker();
    private void btnStart_Click(object sender, RoutedEventArgs e)
    {
        if (!bw.IsBusy)
        {
            bw.DoWork += bw_DoWork;
            bw.RunWorkerCompleted += bw_RunWorkerCompleted;
            bw.ProgressChanged += bw_ProgressChanged;
            bw.RunWorkerAsync();
        }
    }
    private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        this.lblProgress.Content= e.ProgressPercentage.ToString()+ "%";
        this.pb.Value = e.ProgressPercentage;
    }

    private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled)
            lblProgress.Content = "Cancel !";

        else if (e.Error!=null)
            lblProgress.Content= "Error: " + e.Error.Message;

        else
            lblProgress.Content = "Finish !";
    }

    private void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker bw = sender as BackgroundWorker;
        StreamReader sr = new StreamReader("C:\\File 1.txt");
        StreamWriter sw = new StreamWriter("C:\\Out-File 1.txt");

            var fi = new FileInfo("C:\\File 1.txt");
            long total = (char)fi.Length;
            int  i = 0;
            string result;
            while (!sr.EndOfStream)
            {
                if (!bw.CancellationPending)
                {
                    result = sr.ReadLine();
                    sw.WriteLine(result);
                    i = i + (char)result.Length;
                    bw.ReportProgress((int)((decimal)i / (decimal)total * (decimal)100));
                }
                else
                {e.Cancel = true; break;}
            } sw.Close(); sr.Close();
    }

    private void btnCancel_Click(object sender, RoutedEventArgs e)
    {
        if (bw.WorkerSupportsCancellation)
            bw.CancelAsync();
    }
}

【问题讨论】:

  • 即使显示的百分比和以前一样,是否真的需要更新显示的百分比?
  • 所以你是说你永远不会得到任何用户界面更新?如果你在bw_ProgressChanged 的第一行设置断点,它会触发吗?
  • 是的,我知道几个循环的值将保持不变...
  • @Nilzor 是的,它被触发了,我在bw_ProgressChanged 设置了一个断点,但是 WPF 是“冻结”..不能用鼠标移动...

标签: c# wpf backgroundworker


【解决方案1】:

我知道这不是关于后台工作者问题的真正答案,但也许是时候使用名为 async/await 的“新”C# 5.0 功能了?我对您的代码进行了一些修改并对其进行了测试,并且所有工作都没有错误和冻结:

    private bool cancelled;

    private bool started;

    public MainWindow()
    {
        InitializeComponent();
    }

    private async void btnStart_Click(object sender, RoutedEventArgs e)
    {
        if (started) return;
        cancelled = false;
        started = true;
        try
        {
            await DoAsync();
        }
        catch (Exception exception)
        {
            lblProgress.Content = "Error: " + exception.Message;
            return;
        }
        finally
        {
            started = false;
        }
        lblProgress.Content = cancelled ? "Cancel !" : "Finish !";
    }

    private async Task DoAsync()
    {
        using (var sr = new StreamReader("C:\\File 1.txt"))
        {
            using (var sw = new StreamWriter("C:\\Out-File 1.txt"))
            {
                var fi = new FileInfo("C:\\File 1.txt");
                long total = (char) fi.Length;
                int i = 0;
                string result;
                while (!sr.EndOfStream)
                {
                    result = await sr.ReadLineAsync();
                    await sw.WriteLineAsync(result);
                    i = i + (char) result.Length;
                    ProgressChanged((int) ((decimal) i/(decimal) total*(decimal) 100));
                    if (cancelled) return;
                }
            }
        }
    }

    private void ProgressChanged(int progress)
    {
        this.lblProgress.Content = progress.ToString() + "%";
        this.pb.Value = progress;
    }

    private void btnCancel_Click(object sender, RoutedEventArgs e)
    {
        cancelled = true;
    }

另外我对百分比计算的建议是检查它是否在每次迭代中发生变化,然后进行界面更新。

【讨论】:

  • 这是一个很好的答案,但在某些情况下使用 .NET 4.5(以及因此 C# 5)不是一个选项。
  • @Marton Microsoft.Bcl.Async nuget 包呢?
  • 我通常会尽量避免安装任何额外的东西来解决一个可以通过更改我的代码中的一些小东西来明显解决的问题。
【解决方案2】:

问题在于您的后台工作人员的工作部分(即从文件中读取一行并将该行写入另一个文件)是一个非常简短的操作,在这个非常简短的操作之后您更新用户界面。

这意味着您非常频繁地更新用户界面,并且用户界面线程非常忙于进行这些更新。因此它没有机会做你想做的其他事情。

如果您将更新次数限制为 100,您应该会发现用户界面没有锁定。

BackgroundWorker bw = sender as BackgroundWorker;
StreamReader sr = new StreamReader("C:\\File 1.txt");
StreamWriter sw = new StreamWriter("C:\\Out-File 1.txt");

var fi = new FileInfo("C:\\File 1.txt");
long total = (char)fi.Length;
int  i = 0;
string result;
long updateIncrement = total / 100;
long nextUpdate = 0;
while (!sr.EndOfStream)
{
    if (!bw.CancellationPending)
    {
        result = sr.ReadLine();
        sw.WriteLine(result);
        i = i + (char)result.Length;
        if ( i > nextUpdate )
        {
            nextUpdate += updateIncrement;
            bw.ReportProgress((int)((decimal)i / (decimal)total * (decimal)100));
        }
    }
    else
    {
        e.Cancel = true; 
        break;
    }
} 
sw.Close(); 
sr.Close();

顺便说一句,为什么要将文件长度转换为 char 然后将其分配给 long? 如果文件大小大于 0xFFFF,您将遇到问题。

【讨论】:

    【解决方案3】:

    我猜您在工作线程中遇到了 NullReferenceException,因为您没有检查 result != null,当您到达流的末尾时会发生这种情况。

    编辑:我在这里可能是错的 - 我不知道 sr.EndOfStream 在你读完最后一行之后是否为真。此外,即使您遇到异常,runWorkerCompleted 应该也会执行。也就是说,您可能在这里泄漏了流;您应该将读取的流包装在 using 块中。

    【讨论】:

    • 我没有得到任何异常,只是“冻结”了 WPF。
    【解决方案4】:

    用户界面中的冻结通常是由 GUI 线程繁忙引起的。为什么它很忙,我无法从代码中看出。也许你有一些其他的 UI 代码在进程中间的某个地方触发,它循环并且永远不会退出。这将导致进度更新和/或 RunWorkerCompleted 方法永远不会执行(冻结)。

    我也会从找出何时冻结开始。也许这可以给你一些线索。在进度报告前后以及文件打开和关闭前后插入Debug.WriteLine-lines。看看调试输出是什么样子的。与 UI 更新相反,调试写入可以从任何线程触发,因此非常适合调试线程问题。

    【讨论】:

    • 这很有趣,我只是把Debug.WriteLine(total); 放在i = i + (char)result.Length; 之后,WPF 正在工作......但它运行不正确,进度条从无到满,但标签一直运行在 100 以上% 直到数千 %。而且因为太长了,当我停止从VS调试时,输出文件仍然没有完成......
    猜你喜欢
    • 2019-09-28
    • 1970-01-01
    • 1970-01-01
    • 2012-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多