关闭表单时出现ObjectDisposedException

时间:2018-06-20 12:09:43

标签: c# winforms

我在WinForm上有一个计时器,该计时器在表单加载时启动:

private void MainForm_Load(object sender, EventArgs e)    
{
    Action action = () => lblTime.Text = DateTime.Now.ToLongTimeString();

    Task task = new Task(() => {
        while (true)
        {
            Invoke(action);
            Task.Delay(1000);
        }
    });
    task.Start();
}

问题是当我在VS中以调试模式启动应用程序并关闭它时。我得到一个ObjectDisposedException,它声明我的表单已经被处置。

我尝试通过以下方式对其进行修复:

private bool _runningTimer = true;

public MainForm()
{
    InitializeComponent();

    // ...
    FormClosing += MainForm_FormClosing;
}

private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
    _runningTimer = false;
}

private void MainForm_Load(object sender, EventArgs e)
{
    Action action = () => lblTime.Text = DateTime.Now.ToLongTimeString();

    Task task = new Task(() => {
        while (_runningTimer)
        {
            Invoke(action);
            Task.Delay(1000);
        }
    });
    task.Start();
}

但是问题仍然存在。我在这里做错什么了?

更新:我知道有一个适用于WinForms的标准计时器,该计时器在多线程环境中效果很好。我只是想知道如何使其能够更好地理解如何处理比赛条件。这种计时器只是一个示例,它可能是需要更新GUI的另一个过程。

更新2 :在回答Hans PassantInigmativity之后,我来到了该代码:

private void MainForm_Load(object sender, EventArgs e)
{
    Action action = () => { lblTime.Text = DateTime.Now.ToLongTimeString(); };

    Task task = new Task(async () => {
        while (!IsDisposed)
        {
            Invoke(action);
            await Task.Delay(1000);
        }
    });
    task.Start();
}

但是无论如何,如果我设定时间间隔,例如100ms,ObjectDisposedException仍然会抛出。

这不是现实生活中的例子,我只是尝试一下...

2 个答案:

答案 0 :(得分:0)

在您的第一个示例中,任务不知道您的应用正在退出,并且有可能在标签被破坏后 被调用,因此ObjectDisposedException

尽管在第二个示例中,您尝试向任务发出警报,但它实际上不是线程安全的,并且在处置控件之后仍然可以调用该操作。

计时器

更好的解决方案是仅使用WinForms Timer。如果您通过设计器将计时器放置在表单上,​​它将自动将其注册为组件依赖性,从而使生命周期管理变得更加容易。

使用WinForm计时器,您无需担心线程或Task,更重要的是,如果您需要更新UI(而不是子对象),则无需担心Invoke线程或非UI上下文任务)

告诉我更多

答案 1 :(得分:0)

好的,我尝试通过以下方式使用任务取消:

public MainForm()
{
    InitializeComponent();
    cts = new CancellationTokenSource();

    Load += MainForm_Load;
    FormClosing += MainForm_FormClosing;
}

private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
    cts.Cancel();
}

private void MainForm_Load(object sender, EventArgs e)
{        
    CancellationToken ct = cts.Token;

    Action action = () => { lblTime.Text = DateTime.Now.ToLongTimeString(); };

    var task = Task.Factory.StartNew(async () => {
        ct.ThrowIfCancellationRequested();

        while (true)
        {
            Invoke(action);
            await Task.Delay(100);
        }
    }, ct);
}

不知道它是否正确,但是即使将时间间隔设置为10 ms,它似乎也可以正常工作。