我有2个winforms:
Form1是主要形式。 Form1打开Form2。在Form2 Load
事件处理程序中,启动新的后台工作线程。当工作线程完成时,它将通知UI线程更新Form2。
问题是,用户可以在工作线程仍在运行时关闭Form2 。因此Form2可能会在工作线程结束时消失。然后,当工作线程尝试更新Form2 UI时,会发生一些Null Reference Exception。
我打算用一个标志来表示Form2的存在。每次更新UI时,都会检查标志以确保Form2存在。但是这个 check-and-act 模式无法处理竞赛规则。因为表单可能会在之后关闭,但检查通过但 之前会执行UI更新操作。
那么有什么方法可以解决这个问题吗?
Form2的一些代码:
private void StartComputeGraphWorker()
{// this runs on the UI thread.
try
{
this.generationFinished = false;
DisableAllControls(); //prevent user input while some background work is underway.
StartShowProgressMarquee();
ThreadStart d = new ThreadStart(WorkerStartWrapper);
worker = new Thread(d);
worker.IsBackground = true;
worker.Start();
}
catch (Exception ex)
{
Logger.LogMessage(Logger.LogLevel.Error, ex.Message);
EnableAllControls();
StopShowProgressMarquee();
}
}
private void NotifyUI(Boolean suceess)
{
if (suceess)
{
// this is on the secondary illustration form. it may NOT exist by now.
if (!this.formClosed)
{//race conditions here
this.Invoke(new MethodInvoker(ShowGraphDataInUIThread));
}
else//the form has been closed, we have no place to show the graph, just return.
{
return;
}
}
else
{
// this logs to the main input form, it always exists.
Logger.LogMessage(Logger.LogLevel.Warning, "Graph generation failed.");
}
}
private void WorkerStartWrapper()
{
try
{
RenderGraphWorker();
NotifyUI(true);
}
catch (Exception ex) // ThreadAbortException or Other Exceptions
{
Logger.LogMessage(Logger.LogLevel.Warning, ex.Message);
NotifyUI(false);
}
}
我检查了下面的帖子:
How to update the GUI from another thread in C#?
它并不完全相同。我的表格可以不见了。它不仅仅是关于跨线程控制更新。
使用BackgroundWorker方法,在Form2 Closing事件中取消订阅RunWorkerCompleted事件可以解决我的问题。
但我仍然想知道是否可以使用Thread类。
答案 0 :(得分:0)
今天,我重新考虑取消订阅方法。看起来不错。但实际上可能不是。
竞争条件,在我取消订阅时,代码可能正在已完成的事件处理程序中运行。 因此,取消它不会阻止它操纵可能不存在的形式。
我认为我仍然应该坚持标准的BGW范式并以其他方式解决这个问题。
在我的场景中,用户有2种方法可以取消BGW操作。
Cancel
按钮。我目前的解决方案是:
如果用户点击Cacncel
按钮,我会在Cancel
按钮点击处理程序之前显示一些用户界面通知,然后再调用bgw.CancelAsync()
。像这样:
this.label1.Text = "Operation Cancelled";
bgw.CancelAsync()
此时,用户界面 guarenteed 存在。
如果用户关闭表单,我只需在表单结束事件处理程序中调用bgw.CancelAsync()
。然后BGW.DoWork()
将轮询并找到此信号并停止执行。我在这里需要 no 用户界面通知,因为这是用户的隐含意图。
对于两种取消方案,BGW完整事件处理程序包含对取消结果无UI操作。
总而言之,让BGW完成其生命周期。