以下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();
}
}
我知道这并不是关于后台工作程序问题的真正答案,但也许是时候使用名为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;
}
另外,我对百分比计算的建议是,检查它是否在每次迭代中都发生了变化,然后更新界面。
问题是后台工作人员的工作部分(即从文件中读取一行并将该行写入另一个文件)是一个非常简短的操作,在这个非常简短的过程之后,您将更新用户界面。
这意味着您非常频繁地更新用户界面,而用户界面线程正忙于进行这些更新。因此,它没有机会做你想做的其他事情。
如果你将更新次数限制在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,则会出现问题。
我猜您在工作线程中遇到了NullReferenceException,因为您没有检查result != null
,当您到达流的末尾时会发生这种情况。
编辑:我可能错了——我不知道在你读了最后一行之后,sr.EndOfStream是否为真。此外,即使遇到异常,runWorkerCompleted也应该执行。也就是说,你可能正在泄漏这里的溪流;您应该使用块来包装流读取。
用户界面中的冻结通常是由GUI线程繁忙引起的。为什么它很忙,我从代码中看不出来。也许您有一些其他的UI代码在流程中间的某个地方触发,这些代码循环而从不退出。这将导致进度更新和/或RunWorkerCompleted方法永远无法执行(冻结)。
我还将从发现何时冻结开始。也许这能给你一些线索。在进度报告前后以及文件打开和关闭前后插入Debug.WriteLine
行。然后查看调试输出的样子。与UI更新相反,调试写入可以从任何线程触发,因此有利于调试线程问题。