为了账号安全,请及时绑定邮箱和手机立即绑定

关闭表单时出现 ObjectDisposedException

关闭表单时出现 ObjectDisposedException

C#
长风秋雁 2021-08-22 17:59:28
我在 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 Passant和Inigmativity 的答案,我来到了该代码: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();}但无论如何,如果我设置时间间隔,例如 100 毫秒,ObjectDisposedException 仍然会抛出。这不是现实生活中的例子,我只是在试验它......
查看完整描述

2 回答

?
小唯快跑啊

TA贡献1863条经验 获得超2个赞

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


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 毫秒,它似乎也有效。


查看完整回答
反对 回复 2021-08-22
?
幕布斯6054654

TA贡献1876条经验 获得超7个赞

在您的第一个示例中,任务不知道您的应用程序正在退出,并且在标签销毁有调用操作的风险,因此ObjectDisposedException.

尽管您尝试在第二个示例中提醒任务,但它并不是真正的线程安全,并且您仍然可以在处理控件后调用该操作。

计时器

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

使用 WinForm 计时器,您无需担心线程或Tasks,更重要的是,您无需担心Invoke是否需要更新 UI(与子线程或非 UI 上下文任务相反)


查看完整回答
反对 回复 2021-08-22
  • 2 回答
  • 0 关注
  • 191 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信