【发布时间】:2018-05-18 11:59:34
【问题描述】:
我遇到了一个使用后台工作线程来获取数据和更新数据网格的表单的问题。
用例是一个带有进度条的列表,显示数据库中加载了多少表。
按以下顺序进行,
- FormA 实例化 FormB
- FormB 启动了一个后台工作程序,该工作程序循环获取数据和 更新 datagrid.datasource。
它有时运行良好,但其他 FormB.ShowDialog() 会返回空引用异常。我不明白为什么。
这是调用表单代码:
private void button1_Click(object sender, EventArgs e)
{
using (var f = new FormFactProgress(_dwConnectionString, _sourceConnectionString, _etlConnectionString))
{
f.ShowDialog(); \\ THIS IS WHERE THE NULL REFERENCE EXCEPTION ALWAYS HAPPENS!
labelLoadWarning.Visible = false;
groupBoxLoadFacts.Enabled = false;
buttonLoadFacts.BackColor = Color.FromArgb(128, 255, 128);
buttonLoadFacts.Text = "Loaded";
if (dynamicWarehouseCatalog.FactsLoaded(_dwConnectionString) && dynamicWarehouseCatalog.DimsLoaded(_dwConnectionString))
{
groupBoxSync.Enabled = true;
}
}
}
子表单(它是第二个进行数据网格更新的后台工作者):
private void buttonStart_Click(object sender, EventArgs e)
{
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync();
}
backgroundWorker2.DoWork += new DoWorkEventHandler(backgroundWorker2_DoWork);
if (!backgroundWorker2.IsBusy)
{
backgroundWorker2.RunWorkerAsync();
}
buttonStart.Enabled = false;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
BackgroundWorker worker = (BackgroundWorker)sender;
sSISDBCatalog.StartFactLoad(_etlConnectionString);
while (isLoading)
{
System.Threading.Thread.Sleep(1000);
if (dynamicWarehouseCatalog.FactsLoaded(_dwConnectionString))
{
if (dynamicWarehouseCatalog.AllFactsLoaded(_dwConnectionString))
{
isLoading = false;
}
}
}
}
catch(Exception ex)
{
MessageBox.Show(
"Progress viewing has failed. The ETL is still running though. Please monitor your data warehouse growth manually. Error: " + ex, "Pregress Visualisation Failed",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return;
}
}
private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
{
try
{
Control.CheckForIllegalCrossThreadCalls = false;
BackgroundWorker worker = (BackgroundWorker)sender;
var dw = DwData(_dwConnectionString);
var source = SourceData(_sourceConnectionString);
dataGridView1.DataSource = GetProgress(dw, source);
while (isLoading)
{
System.Threading.Thread.Sleep(1000);
dw = DwData(_dwConnectionString);
dataGridView1.DataSource = GetProgress(dw, source);
dataGridView1.Refresh();
}
MessageBox.Show(
"Your data warehouse facts are loaded!", "Facts Loaded",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
}
catch (Exception ex)
{
MessageBox.Show(
"Progress viewing has failed. The ETL is still running though. Please monitor your data warehouse growth manually. Error: " + ex, "Pregress Visualisation Failed",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return;
}
}
【问题讨论】:
-
你为什么使用后台工作,而不是像 async/await 这样的现代方法?
-
@rory.ap 我只是碰巧选择了这个作为解决方案。如果有更好的方法,我很乐意改变它。就像我说的,当这个奇怪的错误没有发生时,它工作得很好。
-
不要直接从后台工作线程更新你的 UI:使用
Invoke或backgroundWorker.ReportProgress()等代替。CheckForIllegalCrossThreadCalls = false是个非常糟糕的主意!对于异常:准确检查堆栈跟踪,f不能为空,或参见:stackoverflow.com/questions/4660142/… -
您需要了解多线程编程和可用的方法,然后才盲目地投入其中。说得客气一点,这是一个高级话题,如果你不明白自己在做什么,就会有无穷无尽的问题和不满意的客户。
-
@EvanBarke 你不能在任何操作系统中从另一个线程修改 UI。这意味着您不能从
DoWork修改网格。您必须处理RunWorkerCompleted事件并在那里修改 UI。此时使用async/await要容易得多。将两个异步操作与 BGW 结合起来也困难得多
标签: c# multithreading winforms backgroundworker