为什么我有时会遇到“无法访问处置对象”的异常?
问题描述:
我有这样的代码在我的背景DoWork
事件:为什么我有时会遇到“无法访问处置对象”的异常?
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (true)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
if (tempCpuValue >= (float?)nud1.Value || tempGpuValue >= (float?)nud1.Value)
{
soundPlay = true;
blinking_label();
NudgeMe();
}
else
{
soundPlay = false;
stop_alarm = true;
}
cpuView();
gpuView();
}
}
}
在cpuView()
方法我有这样的代码:
if (InvokeRequired)
{
this.Invoke(new Action(() => data = new List<string>()));
this.Invoke(new Action(() => data.Add("Gpu Temeprature --- " + sensor.Value.ToString())));
this.Invoke(new Action(() => listBox1.DataSource = null));
this.Invoke(new Action(() => listBox1.DataSource = data));
this.Invoke(new Action(() => listBox1.Invalidate()));
}
异常/错误的这个时候上线:
this.Invoke(new Action(() => data = new List<string>()));
这是我的Form1关闭事件:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (MessageBox.Show("Are you Sure you want to Exit. Click Yes to Confirm and No to continue", "WinForm", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
{
e.Cancel = true;
}
else
{
if (backgroundWorker1.WorkerSupportsCancellation == true)
{
backgroundWorker1.CancelAsync();
}
}
}
一旦我退出我的应用程序,就会发生错误/异常,单击右上角的红色x并选择YES退出。
我该怎么办?
答
因此,这里的一部分问题是您没有充分利用BGW为您提供的功能。如果您从DoWork
以内拨打Invoke
,这意味着您没有真正使用BGW来完成它的工作。就你而言,你应该在BGW上打电话ReportProgress
,然后有一个ProgressChanged
事件处理程序,根据进度更新UI。您可以传入一个代表数据的参数来更新UI,因此您可以在DoWork
内构建您的列表,并通过ReportProgress
传递它,然后在ProgressChanged
事件处理程序中设置DataSource
。
现在BGW负责在工人被取消后不报告进度。
答
由于表单已关闭,其子控件和表单本身已处理。但是,WinForms继续在UI线程上处理未决的调用。这就是为什么它提出了“无法访问处置对象”。
Application.DoEvents()
通常被描述为邪恶,但它的工作是在UI线程上处理所有挂起的消息。
在Close事件中完成后台线程是不够的。
这就是为什么我建议您在任何Dispose之前添加一个Application.DoEvents()
,所以待处理的调用正在刷新,然后表单将优雅地关闭。
if (MessageBox.Show("Are you Sure you want to Exit. Click Yes to Confirm and No to continue", "WinForm", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
{
e.Cancel = true;
}
else
{
if (backgroundWorker1.WorkerSupportsCancellation == true)
{
backgroundWorker1.CancelAsync();
}
Application.DoEvents();
}
在Application.DoEvents的情况下()是用于你太哈克,可以通过使用封装InvokeRequired图案的扩展方法吞下的ObjectDisposedException:
public static void InvokeIfRequired(this Control control, Action action)
{
try
{
if (control.IsDisposed)
return;
if (control.InvokeRequired)
control.Invoke(action);
else
action();
}
catch (ObjectDisposedException)
{
// There is nothing much we can do when an Invoke is pending on a disposed control
// the other exceptions will bubble up normally
}
}
Servy感谢。如果你能告诉我一个如何去做的例子。非常感谢你。 – user2065612 2013-03-07 20:52:51
@ user2065612我已经准确描述了您需要在分步说明中进行的操作。在Web上使用此行为以及后台工作人员的MSDN文档也有大量的详细示例。你不需要我给你完全完整的代码。你应该借此机会学习如何自己学习如何使用这个功能,因为如果你做了一些UI编程,你将需要做很多事情;你只需要能够自己做到这一点。 – Servy 2013-03-07 20:54:57