backgroundworker冻结WPF

时间:2013-12-13 07:32:33

标签: c# wpf backgroundworker

关注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();
    }
}

4 个答案:

答案 0 :(得分:2)

我知道关于后台工作者的问题并不是真正的答案,但也许是时候使用名为 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;
    }

此外,我对百分比计算的建议是检查每次迭代是否发生了变化,然后提升界面更新。

答案 1 :(得分: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,则会出现问题。

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

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

我也会在冻结时找到。也许这可以给你一些线索。在进度报告之前和之后以及文件打开和关闭之前和之后插入Debug.WriteLine行。看看调试输出的样子。与UI更新相反,可以从任何线程触发调试写入,这样可以很好地调试线程问题。