为什么我有时会遇到“无法访问被处置对象”的异常?

时间:2013-03-07 20:40:24

标签: c#

我的后台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并选择“是”退出,就会发生错误/异常。

我该怎么办?

2 个答案:

答案 0 :(得分:1)

因此,部分问题在于您没有利用BGW为您提供的功能。如果您从Invoke内部呼叫DoWork,则意味着您并没有真正使用BGW来完成它的工作。在您的情况下,您应该只是在BGW上调用ReportProgress,然后有一个ProgressChanged事件处理程序,根据进度更新UI。您可以传入代表数据的参数来更新用户界面,以便您可以在DoWork内构建列表,通过ReportProgress传递,然后在DataSource中设置ProgressChanged {{1}}事件处理程序。

现在,BGW负责在工人被取消后不报告进度。

答案 1 :(得分:0)

当一个表格关闭时,它的子控件和表单本身就被处理掉了。但是,WinForms继续在UI线程上处理挂起的Invokes。这就是它引发“无法访问被处置对象”的原因。

Application.DoEvents()通常被描述为邪恶,但它的工作是处理UI线程上的所有待处理消息。

在Close事件中完成后台线程是不够的。

这就是为什么我建议你在任何Dispose之前添加一个Application.DoEvents(),所以正在刷新挂起的Invokes,然后表格将正常关闭。

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
    }
}