我有一个启动System.Threading.Timer的应用程序,然后这个计时器每5秒从一个链接的数据库中读取一些信息,并在应用程序的主窗体上更新图形用户界面;
由于System.Threading.Timer为Tick事件创建了另一个线程,因此我需要使用Object.Invoke来更新应用程序主窗体上的用户界面,代码如下:
this.Invoke((MethodInvoker)delegate()
{
label1.Text = "Example";
});应用程序运行得很好,但有时当用户关闭主窗体,然后关闭应用程序时,如果timer_tick事件上的第二个线程正在更新主线程上的用户界面,则用户将获得一个ObjectDisposedException。
如何在关闭主窗体之前停止和关闭线程计时器并避免对象处理异常?
发布于 2010-09-05 00:18:13
System.Timers.Timer是一个可怕的类。没有可靠地阻止它的好方法,总是有一场竞赛,你无法避免它。问题是它的Elapsed事件是从线程池线程引发的。您无法预测该线程实际开始运行的时间。当您调用Stop()方法时,该线程很可能已经被添加到线程池中,但还没有开始运行。它同时受制于Windows线程调度程序和线程池调度程序。
你甚至不能通过任意延迟关闭窗口来可靠地解决这个问题。在最极端的情况下,线程池调度程序可以将线程的运行延迟高达125秒。您可以通过将收盘延迟几秒来减少出现异常的可能性,它不会为零。将收盘时间推迟2分钟是不现实的。
只是不要用它。使用System.Threading.Timer并使其成为您在事件处理程序中重新启动的一次性计时器。或者使用System.Windows.Forms.Timer,它是同步的。
这里您应该选择WF计时器,因为您使用的是Control.Invoke()。在UI线程空闲之前,委托目标不会开始运行。与WF计时器的行为完全相同。
发布于 2010-09-04 14:29:52
这是一个有点棘手的命题,因为您必须在给定的关闭事件上确保以下内容
我以前遇到过这个问题,我发现预防这个问题是非常有问题的,并且涉及到很多混乱的、难以维护的代码。相反,捕获这种情况下可能出现的异常要容易得多。通常,我通过包装Invoke方法来实现,如下所示
static void Invoke(ISynchronizedInvoke invoke, MethodInvoker del) {
try {
invoke.Invoke(del,null);
} catch ( ObjectDisposedException ) {
// Ignore. Control is disposed cannot update the UI.
}
}如果您对此异常的后果感到满意,那么忽略此异常本身并没有错。这是如果你习惯于UI在它已经被释放之后没有更新的话。我当然是:)
上面的代码并没有处理第二个问题,它仍然需要在你的委托中手动完成。在使用WinForms时,我也经常使用以下重载来删除手动检查。
static void InvokeControlUpdate(Control control, MethodInvoker del) {
MethodInvoker wrapper = () => {
if ( !control.IsDisposed ) {
del();
}
};
try {
control.Invoke(wrapper,null);
} catch ( ObjectDisposedException ) {
// Ignore. Control is disposed cannot update the UI.
}
}便笺
正如Hans所指出的,ObjectDisposedException并不是唯一可以从Invoke方法中引发的异常。您还需要考虑处理其他几个问题,至少包括InvalidOperationException。
发布于 2013-09-06 04:44:17
创建两个布尔值'StopTimer‘和'TimerStopped’。将计时器的AutoReset属性设置为false。然后将经过的方法格式化为以下格式:
TimerStopped = false;
Invoke((MethodInvoker)delegate {
// Work to do here.
});
if (!StopTimer)
timer.Start();
else
TimerStopped = true;这样,您就可以防止竞争条件,检查计时器是否应该继续,并在方法到达末尾时进行报告。
现在按如下方式格式化您的FormClosing事件:
if (!TimerStopped)
{
StopTimer = true;
Thread waiter = new Thread(new ThreadStart(delegate {
while (!TimerStopped) { }
Invoke((MethodInvoker)delegate { Close(); });
}));
waiter.Start();
e.Cancel = true;
}
else
timer.Dispose();如果计时器还没有停止,就会启动一个线程,等待它停止计时器,然后再次尝试关闭窗体。
https://stackoverflow.com/questions/3641147
复制相似问题