为什么我得到一个异常"不能访问已处置的对象"有时

本文关键字:访问 不能 有时 对象 异常 一个 为什么 | 更新日期: 2023-09-27 18:02:55

我有这个代码在我的背景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事件中完成后台线程是不够的。

这就是为什么我建议你在任何处置之前添加一个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()对您来说太复杂,您可以使用封装了invokerrequired模式的扩展方法来吞下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
    }
}