c# - 关闭表单时出现 ObjectDisposedException
问题描述
我在 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 仍然会抛出。
这不是现实生活中的例子,我只是在试验它......
解决方案
在您的第一个示例中,任务不知道您的应用程序正在退出,并且在标签被销毁后有调用操作的风险,因此ObjectDisposedException
.
尽管您尝试在第二个示例中提醒任务,但它并不是真正的线程安全,您仍然可以在释放控件后调用该操作。
计时器
更好的解决方案是只使用 WinForms Timer
。如果您通过设计器将计时器放置在表单上,它会自动将其注册为组件依赖项,从而使生命周期管理更加容易。
使用 WinForm 计时器,您无需担心线程或Task
s,更重要的是,您无需担心Invoke
是否需要更新 UI(与子线程或非 UI 上下文任务相反)
告诉我更多
推荐阅读
- c - 为什么即使输入大于给定的数字类型,scanf 也会返回正值?
- php - PHP stdClass 对象 - foreach
- javascript - TypeError:无法读取未定义 Discord、js 的属性“执行”
- cookies - Prestashop 附属公司的 Cookie 馅料
- spring - 使用 fido 进行 spring security 4 路身份验证,例如 yubikey
- c# - 更改组合框复选框的前景色
- php - 在前端列出所有已完成的 WooCommerce 订单
- c++ - 如何制作用于围绕 z 轴旋转三角形的特征模型矩阵
- r - 闪亮显示来自系统调用的多行文本输出
- c++ - 在二维数组上使用 delete[] 时访问冲突