我在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();
}
但是无论如何,如果我设定时间间隔,例如100ms,ObjectDisposedException仍然会抛出。
这不是现实生活中的例子,我只是尝试一下...
答案 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,它似乎也可以正常工作。