[英]Form freezes despite using BackgroundWorker unless we add a fake sleep
我已經成功地使BackgroundWorker
在WinForm
上完成了工作。 它工作正常,但實際上並非如此。 如下所示,我的表單具有一個listbox
和一個Progress Bar
。 而且我正在使用它們兩者來顯示在for loop
在文件上寫入收件人列表的進度。 我知道這個過程運行得太快了。 每次寫一行時, listbox
顯示“正在從y個收件人中添加x個 ”消息,並刪除其自身的最后一個條目,以免文本過多。 同樣對於每個插入, progress bar
也必須邁出一步。
當我使用Thread.Sleep(1)
在每次寫入中添加1ms延遲時,程序運行正常。 我的表單仍然可以在桌面上移動,並且表單看起來很正常。 但是,當我刪除此睡眠(這是真實的情況)時,該表單將凍結,好像我從未使用過並行的BackgroundWorker
。 我該如何克服這個問題?
這分別是我的BackgroundWorker的DoWork()
和ProgressChanged()
事件:
做工作:
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;
}
}
當您經常調用ReportProgress()時,這是完全正常的。 一個firehose問題 ,您要求UI線程做更多的工作,然后才能夠執行。 一旦執行了一個委托目標,然后又有一個委托目標,它將永遠無法實現。 現在,它停止執行其他任務,以響應輸入和繪制窗口。 僅在分發代表時,它就消耗了100%的核心資源。 調用隊列不斷增長,但是用戶在程序耗盡內存之前就失去了耐心,因此很少發生實際崩潰。
您需要通過不太頻繁地調用ReportProgress()來解決此問題。 請記住,您只需要實現一個目標,就可以使用戶滿意。 這很容易,您的輸出變成每秒約20次更新的不可讀的模糊。 輕松獲得50倍的安全系數。
這是因為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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.