我已成功让BackgroundWorker
在WinForm
上完成工作。它工作正常,但实际上它没有。如下所示,我的表单包含listbox
和Progress Bar
。我正在使用它们来显示在for loop
的文件中写入收件人列表的进度。我知道这个过程运行得太快了。每次写入一行时,listbox
都会显示“添加x个y收件人”消息并删除其自身的最后一个条目,以便不会有太多文本。每次插入时,progress bar
都必须迈出一步。
当我使用一个Thread.Sleep(1)
在每次写入中添加1ms延迟时,程序正常工作。我的表单仍然可以在桌面上移动,表单看起来很正常。但是当我删除这个睡眠时(这是真实场景),表单会冻结,好像我从未使用并行BackgroundWorker
开始。我怎样才能克服这个问题?
以下是我的BackgroundWorker的DoWork()
和ProgressChanged()
事件:
DoWork的:
private void backgroundWorkerConvertDatatableToFile_DoWork(object sender,
DoWorkEventArgs e)
{
try
{
StringBuilder sb = new StringBuilder();
IEnumerable<string> columnNames = dt.Columns.Cast<DataColumn>().
Select(column => column.ColumnName);
sb.AppendLine(string.Join(GetDelimiter(campaignOutputFormat), columnNames));
backgroundWorkerConvertDatatableToFile.ReportProgress(-1,
dt.Rows.Count.ToString() + " recipients found.");
for (int i = 0; i < dt.Rows.Count; i++)
{
IEnumerable<string> fields = dt.Rows[i].ItemArray.Select(
field => field.ToString());
sb.AppendLine(string.Join(GetDelimiter(campaignOutputFormat), fields));
backgroundWorkerConvertDatatableToFile.ReportProgress(i,
string.Format("Adding {0} of {1}...", (i + 1).ToString(), dt.Rows.Count));
Thread.Sleep(1);
}
string outputFile = String.Format("{0}\\{1}.csv",
campaignOutputPath, campaignFileName);
backgroundWorkerConvertDatatableToFile.ReportProgress(0, "Writing to file..");
File.WriteAllText(outputFile, sb.ToString());
convertSuccess = true;
}
catch (Exception ex)
{
logger.Log(LogLevel.Error,
"FileCampaignRunner: ConvertDataTableToCSV", ex.Message);
convertSuccess = false;
}
if (convertSuccess)
{
backgroundWorkerConvertDatatableToFile.ReportProgress(100,
"Write to file successful!");
}
else
{
backgroundWorkerConvertDatatableToFile.ReportProgress(100,
"Error writing to file.");
}
}
ProgressChanged:
private void backgroundWorkerConvertDatatableToFile_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
switch (e.ProgressPercentage)
{
case -1:
listBoxMessages.Items.Add(e.UserState.ToString());
listBoxMessages.Items.Add("");
break;
case 100:
listBoxMessages.Items.RemoveAt(listBoxMessages.Items.Count - 1);
listBoxMessages.Items.Add(e.UserState.ToString());
break;
default:
listBoxMessages.Items.RemoveAt(listBoxMessages.Items.Count - 1);
listBoxMessages.Items.Add(string.Format(e.UserState.ToString()));
progressBar.PerformStep();
break;
}
}
答案 0 :(得分:5)
当您经常调用ReportProgress()时,这是完全正常的。一个 firehose问题,你要求UI线程做更多的工作,然后才能做到。一旦它执行了一个委托目标,那么另一个目标可用,它永远无法赶上。它现在停止执行其他职责,响应输入和绘制窗口。它只是在派遣代表时燃烧100%的核心。调用队列在不受限制的情况下不断增长,但是在程序内存耗尽之前,用户就会没有耐心,因此实际崩溃很少。
您需要通过不频繁地调用ReportProgress()来解决此问题。请记住,您只需要实现一个目标,让用户的目光得到娱乐。这很容易,你的输出变成了每秒20次更新的难以理解的模糊。为您提供50倍的安全保证金。
答案 1 :(得分:-1)
这是因为UI线程阻塞了执行线程(在访问UI控件时)。请尝试以下代码
switch (e.ProgressPercentage)
{
case -1:
listBoxMessages.Invoke(new MethodInvoker(delegate
{
listBoxMessages.Items.Add(e.UserState.ToString());
listBoxMessages.Items.Add("");
}));
break;
case 100:
listBoxMessages.Invoke(new MethodInvoker(delegate
{
listBoxMessages.Items.RemoveAt(listBoxMessages.Items.Count - 1);
listBoxMessages.Items.Add(e.UserState.ToString());
}));
break;
default:
listBoxMessages.Invoke(new MethodInvoker(delegate
{
listBoxMessages.Items.RemoveAt(listBoxMessages.Items.Count - 1);
listBoxMessages.Items.Add(string.Format(e.UserState.ToString()));
}));
progressBar.Invoke(new MethodInvoker(delegate
{
progressBar.PerformStep();
}));
break;
}